<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Eli Bendersky's website</title><link href="https://eli.thegreenplace.net/" rel="alternate"></link><link href="https://eli.thegreenplace.net/feeds/all.atom.xml" rel="self"></link><id>https://eli.thegreenplace.net/</id><updated>2026-06-07T00:38:58-07:00</updated><entry><title>Thoughts on starting new projects with LLM agents</title><link href="https://eli.thegreenplace.net/2026/thoughts-on-starting-new-projects-with-llm-agents/" rel="alternate"></link><published>2026-06-06T17:38:00-07:00</published><updated>2026-06-07T00:38:58-07:00</updated><author><name>Eli Bendersky</name></author><id>tag:eli.thegreenplace.net,2026-06-06:/2026/thoughts-on-starting-new-projects-with-llm-agents/</id><summary type="html">&lt;p&gt;A few months ago I wrote about &lt;a class="reference external" href="https://eli.thegreenplace.net/2026/rewriting-pycparser-with-the-help-of-an-llm/"&gt;using LLM agents to help restructuring one of my
Python projects&lt;/a&gt;.
It's worth beginning by saying that the
rewrite has been successful by all reasonable measures; I've been able to
continue maintaining that project since then without an issue.&lt;/p&gt;
&lt;p&gt;In this post, I …&lt;/p&gt;</summary><content type="html">&lt;p&gt;A few months ago I wrote about &lt;a class="reference external" href="https://eli.thegreenplace.net/2026/rewriting-pycparser-with-the-help-of-an-llm/"&gt;using LLM agents to help restructuring one of my
Python projects&lt;/a&gt;.
It's worth beginning by saying that the
rewrite has been successful by all reasonable measures; I've been able to
continue maintaining that project since then without an issue.&lt;/p&gt;
&lt;p&gt;In this post, I want to discuss another project I've recently completed with
significant help from agents: &lt;a class="reference external" href="https://eli.thegreenplace.net/2026/watgo-a-webassembly-toolkit-for-go/"&gt;watgo&lt;/a&gt;. In
this project many things are different; most notably, it's a from-scratch
project rather than a rewrite, and it uses a different programming language
(Go). This post describes my experience working on the project, and some lessons
learned along the way.&lt;/p&gt;
&lt;div class="section" id="the-process"&gt;
&lt;h2&gt;The process&lt;/h2&gt;
&lt;p&gt;This is a new project, so it required extensive design. I began by iterating on
the design with the agent, with a sketch of the API. For this purpose, I
recommend using a Markdown file &lt;a class="reference external" href="https://github.com/eliben/watgo/blob/main/doc/notes.md"&gt;committed into the repository&lt;/a&gt;
for future reference.&lt;/p&gt;
&lt;p&gt;After that, I started asking the agent to write CLs &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt; in a logical order that
made sense to me, keeping them small
and reviewable (more on this in the next section). Sometimes it's not easy to
have a small CL, and multiple rounds of revision may confuse the agent;
in this case, I commit the CL and then go back and ask the agent to modify
or refactor the code, as much as needed, with separate CLs. In the worst case,
the whole sequence can be reverted if I feel we've taken the wrong direction
(branches could also be helpful here for more complicated scenarios).&lt;/p&gt;
&lt;p&gt;This point is worth reiterating: sometimes a single CL is a huge step forward,
but requires lots of review, cleanup and refactoring to be viable. I've had
multiple instances where an agent produced several days of work in a single
CL, but I then spent hours instructing it to clean up and refactor. Overall,
it's still a productivity gain, just not as much as some pundits would like us
to believe.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="keeping-the-human-in-the-loop"&gt;
&lt;h2&gt;Keeping the human in the loop&lt;/h2&gt;
&lt;p&gt;Given the current state of agent capabilities, I think it's worth splitting
projects into two categories:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Low importance / prototype / throw away projects where deep code
understanding is unnecessary. These can be &amp;quot;vibe-coded&amp;quot; (submitting agent
code without even reviewing it).&lt;/li&gt;
&lt;li&gt;High importance projects that I actually want to maintain; here, vibe-coding
is ill advised and I insist on reviewing and guiding all code the agent
writes before it's submitted (or shortly after, as discussed above).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;watgo&lt;/tt&gt; projects is a clear example of (2): I certainly intend to maintain
this project in the long term, so I insist on code that I understand. With very
few exceptions, no code gets in without full review and often multiple rounds
of revisions.&lt;/p&gt;
&lt;p&gt;Even if the cost for writing code went down, maintaining a project is so much
more than that. It's triaging and fixing bugs, it's thinking through what needs
to be done rather than how to do it, it's keeping the code healthy over time,
and so on. As &lt;a class="reference external" href="https://www.goodreads.com/quotes/273375-everyone-knows-that-debugging-is-twice-as-hard-as-writing"&gt;Brian Kernighan said&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
Everyone knows that debugging is twice as hard as writing a program in the
first place. So if you're as clever as you can be when you write it, how will
you ever debug it?&lt;/blockquote&gt;
&lt;p&gt;Maybe at some point agents will become good enough that projects in category
(2) can be implemented and maintained completely autonomously. Maybe. But
we're certainly not there yet. My hunch is that getting there will require
crossing the AGI line &lt;a class="footnote-reference" href="#footnote-2" id="footnote-reference-2"&gt;[2]&lt;/a&gt;, after which little in our world remains certain.&lt;/p&gt;
&lt;div class="section" id="practical-workflow"&gt;
&lt;h3&gt;Practical workflow&lt;/h3&gt;
&lt;p&gt;If you're using an agent to send an actual PR and only review &lt;em&gt;that&lt;/em&gt;, it's
difficult to be disciplined enough to actually perform a thorough review. I find
the following method to be more reliable:&lt;/p&gt;
&lt;p&gt;I use a CLI agent running locally in my repository, and ask it to update the
code there. In parallel, I have a VSCode window open in the same project, where
I can:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Review the agent's changes using VSCode's diff view&lt;/li&gt;
&lt;li&gt;Make my own tweaks and code changes if needed&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once I'm pleased with the change, I manually create a commit.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="keeping-the-cls-small"&gt;
&lt;h2&gt;Keeping the CLs small&lt;/h2&gt;
&lt;p&gt;As mentioned above, it's imperative to keep making progress in small chunks,
with small enough CLs that a human can fully understand in a single review. It's
very tempting to sprint ahead submitting thousands of lines of code every day,
but this temptation has to be avoided. Coding with an agent is like
speed-reading; yes, you're making more progress, but comprehension suffers
the faster you go.&lt;/p&gt;
&lt;p&gt;Particularly for refactoring, agents still take the shortest route to
destination. It's important to guide them to think about the &amp;quot;big picture&amp;quot; at
all times, find all instances where X is better done as Y, not just a single
place noticed during a review. This is why it's sometimes OK to have
a CL submitted before you fully agree with everything, and go back to it later
for several refactoring rounds. Source control works amazingly well when
pair-coding with agents.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="testing-strategy"&gt;
&lt;h2&gt;Testing strategy&lt;/h2&gt;
&lt;p&gt;It's a key point discussed in every &amp;quot;how to succeed with AI&amp;quot; article, but
still critical enough to reiterate here: a solid testing strategy is absolutely
crucial for success. Agents produce - by far - the best results when they have
a solid test suite to test their code against.&lt;/p&gt;
&lt;p&gt;With the &lt;a class="reference external" href="https://github.com/eliben/pycparser"&gt;pycparser&lt;/a&gt; rewrite, I had
a large existing test suite. For &lt;a class="reference external" href="https://github.com/eliben/watgo"&gt;watgo&lt;/a&gt;,
the very first thing I did was think through how to adapt the test suites of
the &lt;a class="reference external" href="https://github.com/WebAssembly/spec/"&gt;WASM spec&lt;/a&gt; and of the
&lt;a class="reference external" href="https://github.com/WebAssembly/wabt"&gt;wabt project&lt;/a&gt; for my needs.&lt;/p&gt;
&lt;p&gt;If your project doesn't have such tests to rely on, this should be your first
order of business - finding one, or building one from scratch. Beware of
self-reinforcing loops though; it's dangerous to trust agents for both the
tests and the implementations tested against them.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="language-choice-go-for-agent-written-projects"&gt;
&lt;h2&gt;Language choice - Go for agent-written projects&lt;/h2&gt;
&lt;p&gt;Go is a fantastic language for agents to write, because it's designed to be
very readable by humans. The biggest strengths of Go
are exactly what makes the experience of reviewing agent code so positive:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Go changes very infrequently, so you don't have to wonder &amp;quot;are we using the
most modern / idiomatic approach&amp;quot; or &amp;quot;what the hell is this construct&amp;quot;
as often as with other languages (looking at you, Python and TypeScript).&lt;/li&gt;
&lt;li&gt;There are relatively few ways to accomplish the same thing in Go, further
lowering the mental burden.&lt;/li&gt;
&lt;li&gt;The standard library is rich and there's much less need to keep abreast of
the package-everyone-uses du jour.&lt;/li&gt;
&lt;li&gt;In general, Go is designed for readability, with a mild-but-still-strong type
system, uniform formatting, explicit error propagation and opinionated choices
already made for you.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since most of the time spent by humans when using agents is &lt;em&gt;reading&lt;/em&gt; rather
than &lt;em&gt;writing&lt;/em&gt; code, these effects compound and produce a great experience.
Recall the discussion of how some languages are optimized for writability (Perl)
while others are optimized for readability (Go)? Well, when working on a project
with an agent we live in a world of 99% reading vs. 1% writing, so this really
matters.&lt;/p&gt;
&lt;p&gt;I find this aspect really crucial in light of the earlier points made in this
post - namely, keeping the human in the loop by understanding and reviewing
all of the agent's design choices and code.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="final-thoughts"&gt;
&lt;h2&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;If you're working on a subject that's completely new to you, I would strongly
recommend &lt;em&gt;against&lt;/em&gt; the approach described in this post. To really learn
something, you have to work through it from scratch, yourself, reading,
designing, writing the code. Agents don't change this basic fact; even before
agents, if you wanted to learn X, copying it from Stack Overflow or some other
project clearly wasn't the right way to go. Similarly, while agents can be used
as a prop for learning, they cannot learn &lt;em&gt;for you&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;As a corollary, junior engineers should exercise &lt;em&gt;extreme caution&lt;/em&gt; when relying
on LLMs. There's no replacement to hard-won experience and the sweat and tears
of learning new, challenging topics. Learning is supposed to be hard; if it's
too easy, you're probably not learning.&lt;/p&gt;
&lt;p&gt;For senior engineers, agents are a boon; it's a great tool to increase
productivity, avoid the boring stuff, and get unstuck from procrastination; but
only when used judiciously.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;CL stands for Changelist, also known as a &amp;quot;patch&amp;quot; or a &amp;quot;diff&amp;quot; - basically
a standalone commit that touches one or more files. This term originates
from the source control systems Perforce and Subversion.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Programming is the ultimate realization of thought; if machines can
design, produce, maintain and understand code better than humans, it
means they can start improving themselves, which is the definition of
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Technological_singularity"&gt;singularity&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="Go"></category><category term="Machine Learning"></category><category term="WebAssembly"></category></entry><entry><title>Notes on Fourier series</title><link href="https://eli.thegreenplace.net/2026/notes-on-fourier-series/" rel="alternate"></link><published>2026-05-27T19:30:00-07:00</published><updated>2026-05-28T02:29:23-07:00</updated><author><name>Eli Bendersky</name></author><id>tag:eli.thegreenplace.net,2026-05-27:/2026/notes-on-fourier-series/</id><summary type="html">&lt;link rel="stylesheet" href="https://eli.thegreenplace.net/demos/fourier/fourier-plot.css"&gt;&lt;p&gt;The trigonometric Fourier series is a beautiful mathematical theory that
shows how to decompose a periodic function into an infinite sum of
sinusoids. These are my notes on the subject, with some examples and the
connection to linear algebra in Hilbert space.&lt;/p&gt;
&lt;div class="section" id="coefficients-of-fourier-series"&gt;
&lt;h2&gt;Coefficients of Fourier series&lt;/h2&gt;
&lt;p&gt;Let’s assume that …&lt;/p&gt;&lt;/div&gt;</summary><content type="html">&lt;link rel="stylesheet" href="https://eli.thegreenplace.net/demos/fourier/fourier-plot.css"&gt;&lt;p&gt;The trigonometric Fourier series is a beautiful mathematical theory that
shows how to decompose a periodic function into an infinite sum of
sinusoids. These are my notes on the subject, with some examples and the
connection to linear algebra in Hilbert space.&lt;/p&gt;
&lt;div class="section" id="coefficients-of-fourier-series"&gt;
&lt;h2&gt;Coefficients of Fourier series&lt;/h2&gt;
&lt;p&gt;Let’s assume that &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt; is a &lt;em&gt;well-behaved&lt;/em&gt; &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/8dde33545500de38fae974b4b18036def142e9e3.svg" style="height: 12px;" type="image/svg+xml"&gt;2L&lt;/object&gt;-periodic &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt;
function and that we can find coefficients &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/278ab95d3a54aae8eaa25c34af66d93a19b5e75f.svg" style="height: 11px;" type="image/svg+xml"&gt;a_n&lt;/object&gt; and &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/54d608cbef1529c3089b9281aa6dcb16a2e65f1f.svg" style="height: 15px;" type="image/svg+xml"&gt;b_n&lt;/object&gt;
such that:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/628f7339c7c4d164a982b8da9b48b213692dd5b3.svg" style="height: 49px;" type="image/svg+xml"&gt;\[f(x)=\sum_{n=0}^{\infty}\left(a_n cos\frac{n\pi x}{L}+b_n sin\frac{n\pi x}{L}\right)\]&lt;/object&gt;
&lt;p&gt;Then we say that the &lt;em&gt;Fourier series&lt;/em&gt; on the right-hand side converges
to &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt;. We’ll talk more about the assumptions mentioned above
and convergence in the next section.&lt;/p&gt;
&lt;p&gt;Note that when &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/bfbd617aa9bc9d5a8b0b006de9d1d45d8e688c8d.svg" style="height: 12px;" type="image/svg+xml"&gt;n=0&lt;/object&gt;, the sum becomes just &lt;img alt="a_0" class="valign-m3" src="https://eli.thegreenplace.net/images/math/4a5997da73aadd118038761e69d01e24586bf958.png" style="height: 11px;" /&gt;; therefore
it’s customary to write the series starting with &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/01ea4b6bd17ee603696dd6e63b08b3ba75b78dce.svg" style="height: 12px;" type="image/svg+xml"&gt;n=1&lt;/object&gt;, with a
separate constant component (which is the function's average over
one period). To make computations nicer, this constant is typically
called &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/770db1916542e8d8eb0e85082134672f66f2544a.svg" style="height: 19px;" type="image/svg+xml"&gt;a_0 / 2&lt;/object&gt;, so:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/af4acda237be7c5348b05201f8f6f6c6b75e30b9.svg" style="height: 49px;" type="image/svg+xml"&gt;\[f(x)=\frac{a_0}{2}+\sum_{n=1}^{\infty}\left(a_n cos\frac{n\pi x}{L}+b_n sin\frac{n\pi x}{L}\right)\]&lt;/object&gt;
&lt;p&gt;Our goal is to find the coefficients &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/278ab95d3a54aae8eaa25c34af66d93a19b5e75f.svg" style="height: 11px;" type="image/svg+xml"&gt;a_n&lt;/object&gt; and &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/54d608cbef1529c3089b9281aa6dcb16a2e65f1f.svg" style="height: 15px;" type="image/svg+xml"&gt;b_n&lt;/object&gt; that
satisfy this equation. We’ll do this in three steps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Integrate both sides of the equation between &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/c20a39eb3af4897aa4dea2bd9a1ba1a16fa16352.svg" style="height: 12px;" type="image/svg+xml"&gt;-L&lt;/object&gt; and
&lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/d160e0986aca4714714a16f29ec605af90be704d.svg" style="height: 12px;" type="image/svg+xml"&gt;L&lt;/object&gt;  &lt;a class="footnote-reference" href="#footnote-2" id="footnote-reference-2"&gt;[2]&lt;/a&gt;.&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/d846619340aeef0b7af2015c25413e6d9947e659.svg" style="height: 50px;" type="image/svg+xml"&gt;\[\int_{-L}^{L}f(x)dx=\int_{-L}^{L}\frac{a_0}{2}dx+\sum_{n=1}^{\infty}\bigg (\int_{-L}^{L}a_n cos\frac{n\pi x}{L}dx+\int_{-L}^{L}b_n sin\frac{n\pi x}{L}dx\bigg )\]&lt;/object&gt;
&lt;p&gt;Per Appendix A, all integrals within the sum are zero, so we’re left
with:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/63c90dd5684c96b1f14a49bfb6c7a6dd7e8fcbd3.svg" style="height: 47px;" type="image/svg+xml"&gt;\[\int_{-L}^{L}f(x)dx=\int_{-L}^{L}\frac{a_0}{2}dx=\bigg[\frac{x\cdot a_0}{2}\bigg]_{-L}^{L}=a_0\cdot L\]&lt;/object&gt;
&lt;p&gt;And thus we find &lt;img alt="a_0" class="valign-m3" src="https://eli.thegreenplace.net/images/math/4a5997da73aadd118038761e69d01e24586bf958.png" style="height: 11px;" /&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/455f8d7d3f17720a2f2c577004b50b71999e5ac8.svg" style="height: 44px;" type="image/svg+xml"&gt;\[a_0=\frac{1}{L}\int_{-L}^{L}f(x)dx\]&lt;/object&gt;
&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Multiply both sides by &lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/32329deb8300f4dcae316379bcc624efe02d10d9.svg" style="height: 19px;" type="image/svg+xml"&gt;cos\frac{m\pi x}{L}&lt;/object&gt;
(&lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/6b0d31c0d563223024da45691584643ac78c96e8.svg" style="height: 8px;" type="image/svg+xml"&gt;m&lt;/object&gt; is a positive integer constant) and integrate between
&lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/c20a39eb3af4897aa4dea2bd9a1ba1a16fa16352.svg" style="height: 12px;" type="image/svg+xml"&gt;-L&lt;/object&gt; and &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/d160e0986aca4714714a16f29ec605af90be704d.svg" style="height: 12px;" type="image/svg+xml"&gt;L&lt;/object&gt;.&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/1584e8cb2c704d84ba6e4d6fab63a999698b1e13.svg" style="height: 102px;" type="image/svg+xml"&gt;\[\begin{aligned}
    \int_{-L}^{L}f(x)cos\frac{m\pi x}{L}dx&amp;amp;=\int_{-L}^{L}\frac{a_0}{2}cos\frac{m\pi x}{L}dx\\
    &amp;amp;+\sum_{n=1}^{\infty}\bigg (\int_{-L}^{L}a_n cos\frac{n\pi x}{L}cos\frac{m\pi x}{L}dx+\int_{-L}^{L}b_n sin\frac{n\pi x}{L}cos\frac{m\pi x}{L}dx\bigg )
\end{aligned}\]&lt;/object&gt;
&lt;p&gt;Looking at the right-hand side, the first integral is zero per Appendix
A, and the last integral is zero per Appendix B. We’re left with:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/0bc1f95f24482ce2b1292415f2d9759ed6ac9b3c.svg" style="height: 49px;" type="image/svg+xml"&gt;\[\int_{-L}^{L}f(x)cos\frac{m\pi x}{L}dx=\sum_{n=1}^{\infty}\int_{-L}^{L}a_n cos\frac{n\pi x}{L}cos\frac{m\pi x}{L}dx\]&lt;/object&gt;
&lt;p&gt;Per Appendix B, the integral on the right is zero for all
&lt;object class="valign-m4" data="https://eli.thegreenplace.net/images/math/3ae4a06555822644174f9ff9419084466b3f7aa4.svg" style="height: 17px;" type="image/svg+xml"&gt;n\neq m&lt;/object&gt;, and &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/d160e0986aca4714714a16f29ec605af90be704d.svg" style="height: 12px;" type="image/svg+xml"&gt;L&lt;/object&gt; for &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/193bdae8b42385001cd707bc207b1d95bd6dc2b2.svg" style="height: 8px;" type="image/svg+xml"&gt;n=m&lt;/object&gt;. Therefore, we can write:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/67f393e48f6792c8ed7fb23b3388496540507fab.svg" style="height: 44px;" type="image/svg+xml"&gt;\[\int_{-L}^{L}f(x)cos\frac{m\pi x}{L}dx=a_m\cdot L\]&lt;/object&gt;
&lt;p&gt;Recall that &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/6b0d31c0d563223024da45691584643ac78c96e8.svg" style="height: 8px;" type="image/svg+xml"&gt;m&lt;/object&gt; is an arbitrary integer, just like &lt;img alt="n" class="valign-0" src="https://eli.thegreenplace.net/images/math/d1854cae891ec7b29161ccaf79a24b00c274bdaa.png" style="height: 8px;" /&gt;; for
consistency, we’ll replace &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/6b0d31c0d563223024da45691584643ac78c96e8.svg" style="height: 8px;" type="image/svg+xml"&gt;m&lt;/object&gt; by &lt;img alt="n" class="valign-0" src="https://eli.thegreenplace.net/images/math/d1854cae891ec7b29161ccaf79a24b00c274bdaa.png" style="height: 8px;" /&gt; and isolate
&lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/278ab95d3a54aae8eaa25c34af66d93a19b5e75f.svg" style="height: 11px;" type="image/svg+xml"&gt;a_n&lt;/object&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/f2e9e0215460f6630905ed01ddfbec5116473b88.svg" style="height: 44px;" type="image/svg+xml"&gt;\[a_n=\frac{1}{L}\int_{-L}^{L}f(x)cos\frac{n\pi x}{L}dx\]&lt;/object&gt;
&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Hopefully it’s clear where this is going now; multiply both
sides by &lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/8e9b9f212b20b5a5c03a472debd56362a9c7a608.svg" style="height: 19px;" type="image/svg+xml"&gt;sin\frac{m\pi x}{L}&lt;/object&gt; and integrate between &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/c20a39eb3af4897aa4dea2bd9a1ba1a16fa16352.svg" style="height: 12px;" type="image/svg+xml"&gt;-L&lt;/object&gt;
and &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/d160e0986aca4714714a16f29ec605af90be704d.svg" style="height: 12px;" type="image/svg+xml"&gt;L&lt;/object&gt;. Using a very similar reasoning to step 2, we’ll end up
with:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/418eef7e6ac0ffd84fc9d87246f1329d17cb6d4c.svg" style="height: 44px;" type="image/svg+xml"&gt;\[b_n=\frac{1}{L}\int_{-L}^{L}f(x)sin\frac{n\pi x}{L}dx\]&lt;/object&gt;
&lt;p&gt;We’ve just found a way to calculate all the coefficients of our Fourier
series for &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/af4acda237be7c5348b05201f8f6f6c6b75e30b9.svg" style="height: 49px;" type="image/svg+xml"&gt;\[f(x)=\frac{a_0}{2}+\sum_{n=1}^{\infty}\left(a_n cos\frac{n\pi x}{L}+b_n sin\frac{n\pi x}{L}\right)\]&lt;/object&gt;
&lt;p&gt;Where:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/425afbd1b4528c1480d81e007c564dc8fdaf02b8.svg" style="height: 148px;" type="image/svg+xml"&gt;\[\begin{aligned}
    a_0&amp;amp;=\frac{1}{L}\int_{-L}^{L}f(x)dx\\
    a_n&amp;amp;=\frac{1}{L}\int_{-L}^{L}f(x)cos\frac{n\pi x}{L}dx\\
    b_n&amp;amp;=\frac{1}{L}\int_{-L}^{L}f(x)sin\frac{n\pi x}{L}dx
\end{aligned}\]&lt;/object&gt;
&lt;/div&gt;
&lt;div class="section" id="conditions-on-f-and-convergence-of-fourier-series"&gt;
&lt;h2&gt;Conditions on &lt;em&gt;f&lt;/em&gt; and convergence of Fourier series&lt;/h2&gt;
&lt;p&gt;The previous section discusses Fourier series for a function
&lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt; that is &lt;em&gt;well-behaved&lt;/em&gt; - but what does that mean? The full
answer would lead us deep into analysis, which I’d like to avoid here.
So I’ll keep it brief.&lt;/p&gt;
&lt;p&gt;We typically assume that &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt; is &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Square-integrable_function"&gt;square
integrable&lt;/a&gt;,
which is denoted as &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/f1aae083af2c79348dd78712847ebd55537fa6e6.svg" style="height: 15px;" type="image/svg+xml"&gt;L^2&lt;/object&gt;. Moreover, we assume that the function
is &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Piecewise_function"&gt;piecewise
smooth&lt;/a&gt;: each
segment of the function has continuous derivatives. A very simple
example of a piecewise smooth function is &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/b984af7493f86f1c75ccaa101e16d173cf00622f.svg" style="height: 19px;" type="image/svg+xml"&gt;f(x)=|x|&lt;/object&gt;. Another is
the triangular wave function used in the example below.&lt;/p&gt;
&lt;p&gt;These conditions hold for pretty much any reasonable function we want to
approximate using Fourier series, so they aren’t a serious burden.&lt;/p&gt;
&lt;p&gt;For a function &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt; that satisfies these conditions, it’s
guaranteed to have a Fourier series that &lt;em&gt;pointwise converges&lt;/em&gt; to it.
This means that at every continuous point of &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt;, the Fourier
series converges to it exactly; at every jump point, the Fourier series
converges to the mid-point of the jump.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="cosine-and-sine-series"&gt;
&lt;h2&gt;Cosine and Sine series&lt;/h2&gt;
&lt;p&gt;Sometimes, additional properties of the function &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt; can help
us simplify the Fourier series for it. If &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/945519cc8666a0a6bfd3a390a10cd7fb2facc3bc.svg" style="height: 19px;" type="image/svg+xml"&gt;f_e(x)&lt;/object&gt; is an &lt;a class="reference external" href="https://eli.thegreenplace.net/2025/notes-on-even-and-odd-functions/"&gt;even
function&lt;/a&gt;,
then we know that:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/86ec9b5d04ec9bfba3910311d88ecf40e4379983.svg" style="height: 44px;" type="image/svg+xml"&gt;\[b_n=\frac{1}{L}\int_{-L}^{L}f(x)sin\frac{n\pi x}{L}dx=0\]&lt;/object&gt;
&lt;p&gt;Because the function inside the integral is odd, and integrating an
odd function over a symmetric interval results in 0.&lt;/p&gt;
&lt;p&gt;Therefore, the Fourier series for such &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/945519cc8666a0a6bfd3a390a10cd7fb2facc3bc.svg" style="height: 19px;" type="image/svg+xml"&gt;f_e(x)&lt;/object&gt; is a &lt;em&gt;cosine
series&lt;/em&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/abf9730ce5c3f88d7dedd5fbfa57a0b725b7f0d7.svg" style="height: 49px;" type="image/svg+xml"&gt;\[f_e(x)=\frac{a_0}{2}+\sum_{n=1}^{\infty}a_n cos\frac{n\pi x}{L}\]&lt;/object&gt;
&lt;p&gt;With coefficients &lt;img alt="a_0" class="valign-m3" src="https://eli.thegreenplace.net/images/math/4a5997da73aadd118038761e69d01e24586bf958.png" style="height: 11px;" /&gt; and &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/278ab95d3a54aae8eaa25c34af66d93a19b5e75f.svg" style="height: 11px;" type="image/svg+xml"&gt;a_n&lt;/object&gt; given as before.&lt;/p&gt;
&lt;p&gt;Similarly if &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/7616404fc7a2da1d8602d2c3ed0da2e99e4fb021.svg" style="height: 19px;" type="image/svg+xml"&gt;f_o(x)&lt;/object&gt; is an &lt;em&gt;odd&lt;/em&gt; function, then its &lt;img alt="a_0" class="valign-m3" src="https://eli.thegreenplace.net/images/math/4a5997da73aadd118038761e69d01e24586bf958.png" style="height: 11px;" /&gt;
and &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/278ab95d3a54aae8eaa25c34af66d93a19b5e75f.svg" style="height: 11px;" type="image/svg+xml"&gt;a_n&lt;/object&gt; are 0, and its Fourier series is a &lt;em&gt;sine series&lt;/em&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/a4517fb01f03c4e5b739f7c626668c3b804c5efc.svg" style="height: 49px;" type="image/svg+xml"&gt;\[f_o(x)=\sum_{n=1}^{\infty}b_n sin\frac{n\pi x}{L}\]&lt;/object&gt;
&lt;/div&gt;
&lt;div class="section" id="fourier-series-for-a-non-periodic-function-defined-on-an-interval"&gt;
&lt;h2&gt;Fourier series for a non-periodic function defined on an interval&lt;/h2&gt;
&lt;p&gt;So far we’ve been talking about &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/8dde33545500de38fae974b4b18036def142e9e3.svg" style="height: 12px;" type="image/svg+xml"&gt;2L&lt;/object&gt;-periodic functions that can
be faithfully represented by Fourier series. But what if we have a
non-periodic function defined on a finite interval?&lt;/p&gt;
&lt;p&gt;E.g. suppose we have &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/f1dbdd3a5b38fafd9f78c329171bfe72dea6faf8.svg" style="height: 19px;" type="image/svg+xml"&gt;f(x)=x&lt;/object&gt; on the interval &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/6341a223ef0a566153beb0037d5a48d2d4b9d0eb.svg" style="height: 18px;" type="image/svg+xml"&gt;[0,L]&lt;/object&gt;. Can
we approximate it with a Fourier series?&lt;/p&gt;
&lt;p&gt;Yes! First, we have to make a choice of how to extend the function to
the negative interval &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/e345009799354a67cfdba7a7a8ea8ac4acb4d269.svg" style="height: 18px;" type="image/svg+xml"&gt;[-L,0]&lt;/object&gt;. Then, we simply repeat the
function every &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/8dde33545500de38fae974b4b18036def142e9e3.svg" style="height: 12px;" type="image/svg+xml"&gt;2L&lt;/object&gt; - this is called a &lt;em&gt;periodic extension&lt;/em&gt;. Note
that the Fourier series calculation only cares about the range
&lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/5f1297a5a8758acc19fd4c325ae19878e7e2fbab.svg" style="height: 18px;" type="image/svg+xml"&gt;[-L,L]&lt;/object&gt;. The resulting series will approximate the generated
periodic function in its entirety, and in particular will also converge
to it in the &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/6341a223ef0a566153beb0037d5a48d2d4b9d0eb.svg" style="height: 18px;" type="image/svg+xml"&gt;[0,L]&lt;/object&gt; interval (except maybe the endpoints,
depending on the mode of extension).&lt;/p&gt;
&lt;p&gt;There are several natural ways to extend a function defined on
&lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/6341a223ef0a566153beb0037d5a48d2d4b9d0eb.svg" style="height: 18px;" type="image/svg+xml"&gt;[0,L]&lt;/object&gt; into the interval &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/e345009799354a67cfdba7a7a8ea8ac4acb4d269.svg" style="height: 18px;" type="image/svg+xml"&gt;[-L,0]&lt;/object&gt; &lt;a class="footnote-reference" href="#footnote-3" id="footnote-reference-3"&gt;[3]&lt;/a&gt;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Direct periodic repetition: we simply repeat &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt; every
&lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/d160e0986aca4714714a16f29ec605af90be704d.svg" style="height: 12px;" type="image/svg+xml"&gt;L&lt;/object&gt;: &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/2168e6b40692df7e2d65d2c56516bd3161cf949b.svg" style="height: 19px;" type="image/svg+xml"&gt;f(x+L)=f(x)\ \forall x&lt;/object&gt;.&lt;/li&gt;
&lt;li&gt;Even extension: &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/7e3975e4586db92c8a5b4b1066af313951344cc0.svg" style="height: 19px;" type="image/svg+xml"&gt;f(|x|)&lt;/object&gt;&lt;/li&gt;
&lt;li&gt;Odd extension: &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt; when &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/e7fbdc98286846014db4fa4f5ef46d5d976c75d5.svg" style="height: 15px;" type="image/svg+xml"&gt;x\ge 0&lt;/object&gt; and
&lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/3c4482de6e0520991510d97cf67550d03e9e813b.svg" style="height: 19px;" type="image/svg+xml"&gt;-f(-x)&lt;/object&gt; when &lt;object class="valign-m2" data="https://eli.thegreenplace.net/images/math/92e0c7b6f34838a90088599672030c1921292807.svg" style="height: 14px;" type="image/svg+xml"&gt;x&amp;lt;0&lt;/object&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here’s an example of extending our sample function &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/f1dbdd3a5b38fafd9f78c329171bfe72dea6faf8.svg" style="height: 19px;" type="image/svg+xml"&gt;f(x)=x&lt;/object&gt; onto
the full interval &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/5f1297a5a8758acc19fd4c325ae19878e7e2fbab.svg" style="height: 18px;" type="image/svg+xml"&gt;[-L,L]&lt;/object&gt; and then repeating it periodically
every &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/8dde33545500de38fae974b4b18036def142e9e3.svg" style="height: 12px;" type="image/svg+xml"&gt;2L&lt;/object&gt;:&lt;/p&gt;
&lt;div class="fourier-plot-wrap"&gt;
  &lt;canvas id="function-extensions" class="fourier-plot-canvas" width="760" height="1280"&gt;
  Your browser does not support the HTML5 canvas tag.
  &lt;/canvas&gt;
&lt;/div&gt;&lt;p&gt;Note that the Fourier series for these extended functions will be
different. However, they will all converge to &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt; in the
interval &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/6341a223ef0a566153beb0037d5a48d2d4b9d0eb.svg" style="height: 18px;" type="image/svg+xml"&gt;[0,L]&lt;/object&gt;. Typically, even and odd extensions have the
benefit of producing either cosine or sine series, correspondingly (as
discussed in the previous section).&lt;/p&gt;
&lt;p&gt;We’ve seen that Fourier series work well for periodic functions and also
non-periodic functions defined on a finite domain (because we can extend
these periodically). But what about aperiodic functions defined on the
entire real line? This is where we’ll have to leave Fourier series
behind and move on to their generalization - the &lt;em&gt;Fourier transform&lt;/em&gt;;
this will be a topic for a separate post.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="example"&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;Let’s take the following triangular function &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/75a7147566fd4ba98fe5d85fe008fab6d58d3968.svg" style="height: 19px;" type="image/svg+xml"&gt;t(x)&lt;/object&gt; &lt;a class="footnote-reference" href="#footnote-4" id="footnote-reference-4"&gt;[4]&lt;/a&gt;:&lt;/p&gt;
&lt;div class="fourier-plot-wrap"&gt;
  &lt;canvas id="triangle-wave" class="fourier-plot-canvas" width="760" height="510"&gt;
  Your browser does not support the HTML5 canvas tag.
  &lt;/canvas&gt;
&lt;/div&gt;&lt;p&gt;&lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/75a7147566fd4ba98fe5d85fe008fab6d58d3968.svg" style="height: 19px;" type="image/svg+xml"&gt;t(x)&lt;/object&gt; is periodic with period 4. We can define it by starting
with a formula on the interval &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/4223e7fe7292caec8543b085e1c5e0f283d99e07.svg" style="height: 18px;" type="image/svg+xml"&gt;[0,2]&lt;/object&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/219270dd9765adc74c363412ff71219137e90f0c.svg" style="height: 54px;" type="image/svg+xml"&gt;\[t(x)=
\begin{cases}
    x     &amp;amp;  0 \leq x \leq 1 \\
    2-x   &amp;amp;  1 &amp;lt; x \leq 2  \\
\end{cases}\]&lt;/object&gt;
&lt;p&gt;Then making an odd extension into &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/8291e9956dc145c0d40503664cdb2aa8907faeec.svg" style="height: 18px;" type="image/svg+xml"&gt;[-2,0]&lt;/object&gt; and repeating it
periodically. Now we can go ahead to calculate its Fourier coefficients.&lt;/p&gt;
&lt;p&gt;Since this function is odd, we know that we’ll get a &lt;em&gt;sine series&lt;/em&gt;, as
&lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/278ab95d3a54aae8eaa25c34af66d93a19b5e75f.svg" style="height: 11px;" type="image/svg+xml"&gt;a_n&lt;/object&gt; are going to be 0 for all &lt;img alt="n" class="valign-0" src="https://eli.thegreenplace.net/images/math/d1854cae891ec7b29161ccaf79a24b00c274bdaa.png" style="height: 8px;" /&gt;. Let’s calculate
&lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/54d608cbef1529c3089b9281aa6dcb16a2e65f1f.svg" style="height: 15px;" type="image/svg+xml"&gt;b_n&lt;/object&gt;; in our case &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/61b388ffea23456a34a8c200e35a624fb3500600.svg" style="height: 12px;" type="image/svg+xml"&gt;L=2&lt;/object&gt; (half the period).&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/842ae33a5aa7760177bcac4d6a4ce14bdb3ad3a3.svg" style="height: 44px;" type="image/svg+xml"&gt;\[b_n=\frac{1}{2}\int_{-2}^{2}t(x)sin\frac{n\pi x}{2}dx\]&lt;/object&gt;
&lt;p&gt;Since &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/75a7147566fd4ba98fe5d85fe008fab6d58d3968.svg" style="height: 19px;" type="image/svg+xml"&gt;t(x)&lt;/object&gt; is odd and so is the sine, we’re integrating an even
function over a symmetric interval. Therefore, we only have to integrate
on the positive half of the range and multiply the result by two:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/d434617a027c2c9c6c40fda3d5df9efd71d9da5e.svg" style="height: 44px;" type="image/svg+xml"&gt;\[b_n=\int_{0}^{2}t(x)sin\frac{n\pi x}{2}dx\]&lt;/object&gt;
&lt;p&gt;Let’s set &lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/0845fe4b9330ab55e9477397cf6a0372b91d9490.svg" style="height: 19px;" type="image/svg+xml"&gt;k=\frac{n\pi}{2}&lt;/object&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/da138d7dfb7e49cbba7719c34fa84063eacac079.svg" style="height: 44px;" type="image/svg+xml"&gt;\[b_n=\int_{0}^{2}t(x)sin(kx)dx\]&lt;/object&gt;
&lt;p&gt;And split up the integral for the different segments of &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/75a7147566fd4ba98fe5d85fe008fab6d58d3968.svg" style="height: 19px;" type="image/svg+xml"&gt;t(x)&lt;/object&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/c520bcc563be425bf5eca3c72ccc1dd315148e9d.svg" style="height: 44px;" type="image/svg+xml"&gt;\[b_n=\int_{0}^{1}x\cdot sin(kx)dx+\int_{1}^{2}(2-x)sin(kx)dx\]&lt;/object&gt;
&lt;p&gt;The first integral, by the method described in Appendix C:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/f0ab0ff7e24e7e2488aca6dc7b26a5876f95edca.svg" style="height: 48px;" type="image/svg+xml"&gt;\[I_1=\int_{0}^{1}x\cdot sin(kx)dx=\bigg[\frac{-x cos(kx)}{k}+\frac{sin(kx)}{k^2} \bigg]_{0}^{1}=\frac{sin(k)}{k^2}-\frac{cos(k)}{k}\]&lt;/object&gt;
&lt;p&gt;The second integral can also be split into two:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/f9bbd9960bbe4f5b7689ca32176c299b111ba79c.svg" style="height: 44px;" type="image/svg+xml"&gt;\[I_2=\int_{1}^{2}2sin(kx)dx - \int_{1}^{2}x\cdot sin(kx)dx\]&lt;/object&gt;
&lt;p&gt;The first of these is trivial to calculate; the second can once again
use Appendix C. After some tedious but straightforward calculations &lt;a class="footnote-reference" href="#footnote-5" id="footnote-reference-5"&gt;[5]&lt;/a&gt;
we’ll get:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/adac1720b14c2fe0786b54bc27aae1aef0fbef48.svg" style="height: 38px;" type="image/svg+xml"&gt;\[I_2=\frac{cos(k)}{k}+\frac{sin(k)-sin(2k)}{k^2}\]&lt;/object&gt;
&lt;p&gt;Adding &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/e1d59f2ede2e9c62de3e7071e2f8fe5034aed288.svg" style="height: 15px;" type="image/svg+xml"&gt;I_1+I_2&lt;/object&gt;, we get:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/15e4c796f5a29a762fec37d78ae7c706f4be0ea5.svg" style="height: 83px;" type="image/svg+xml"&gt;\[\begin{aligned}
b_n=I_1+I_2&amp;amp;=\frac{sin(k)}{k^2}-\frac{cos(k)}{k}+\frac{cos(k)}{k}+\frac{sin(k)-sin(2k)}{k^2}\\
&amp;amp;=\frac{2sin(k)-sin(2k)}{k^2}
\end{aligned}\]&lt;/object&gt;
&lt;p&gt;Now let’s substitute &lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/0845fe4b9330ab55e9477397cf6a0372b91d9490.svg" style="height: 19px;" type="image/svg+xml"&gt;k=\frac{n\pi}{2}&lt;/object&gt; back. This makes
&lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/bb77ea3f09c1bd70e8755e99c100f6240fbbaee2.svg" style="height: 19px;" type="image/svg+xml"&gt;sin(2k)&lt;/object&gt; zero because the sine of an integer multiple of
&lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/6ac47b6d7372b4087583cfd048d20f4c1571f5cf.svg" style="height: 8px;" type="image/svg+xml"&gt;\pi&lt;/object&gt; is always zero:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/0dd6f332d6b02605b05f4ec20712ba682a73f754.svg" style="height: 49px;" type="image/svg+xml"&gt;\[b_n=\frac{2sin \frac{n\pi}{2}}{\left (\frac{n\pi}{2}\right )^2}=\frac{8sin \frac{n\pi}{2}}{n^2\pi^2}\]&lt;/object&gt;
&lt;p&gt;We have &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/54d608cbef1529c3089b9281aa6dcb16a2e65f1f.svg" style="height: 15px;" type="image/svg+xml"&gt;b_n&lt;/object&gt;, so the Fourier series for our &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/75a7147566fd4ba98fe5d85fe008fab6d58d3968.svg" style="height: 19px;" type="image/svg+xml"&gt;t(x)&lt;/object&gt; is:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/c3d02d270ba13c4401ff78e0d65bbab33d27fa8f.svg" style="height: 49px;" type="image/svg+xml"&gt;\[t(x)=\sum_{n=1}^{\infty}\frac{8}{n^2\pi^2}sin\frac{n\pi}{2}sin\frac{n\pi x}{2}\]&lt;/object&gt;
&lt;p&gt;Note that for even values of &lt;img alt="n" class="valign-0" src="https://eli.thegreenplace.net/images/math/d1854cae891ec7b29161ccaf79a24b00c274bdaa.png" style="height: 8px;" /&gt;, &lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/4285416a9a4c5ba91639c3d1f777c6de55ce72c2.svg" style="height: 19px;" type="image/svg+xml"&gt;sin \frac{n\pi}{2}&lt;/object&gt; is
zero, so only the odd terms remain:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/7942a20004aaf07d850b012826c56720f6efa3ce.svg" style="height: 42px;" type="image/svg+xml"&gt;\[t(x)=\frac{8}{\pi^2}\bigg[ sin\frac{\pi x}{2}-\frac{1}{3^2} sin\frac{3\pi x}{2}+\frac{1}{5^2}sin\frac{5\pi x}{2}-\cdots\bigg]\]&lt;/object&gt;
&lt;p&gt;Here’s an interactive chart showing how the series &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/75a7147566fd4ba98fe5d85fe008fab6d58d3968.svg" style="height: 19px;" type="image/svg+xml"&gt;t(x)&lt;/object&gt;
converges to our triangular function. You can set the number of terms in
the Fourier series and see the effect (red line). Note that all even
coefficients are zero so it will look the same for &lt;img alt="n" class="valign-0" src="https://eli.thegreenplace.net/images/math/d1854cae891ec7b29161ccaf79a24b00c274bdaa.png" style="height: 8px;" /&gt; as for
&lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/81f998c5713fded10a77db292e4114ec87997543.svg" style="height: 12px;" type="image/svg+xml"&gt;n-1&lt;/object&gt; when &lt;img alt="n" class="valign-0" src="https://eli.thegreenplace.net/images/math/d1854cae891ec7b29161ccaf79a24b00c274bdaa.png" style="height: 8px;" /&gt; is odd.&lt;/p&gt;
&lt;div class="fourier-plot-wrap"&gt;
  &lt;canvas id="triangle-fourier" class="fourier-plot-canvas" width="760" height="510"&gt;
  Your browser does not support the HTML5 canvas tag.
  &lt;/canvas&gt;

  &lt;div class="fourier-plot-controls"&gt;
    &lt;div class="fourier-plot-grid"&gt;
      &lt;label class="fourier-plot-label" for="triangle-fourier-terms"&gt;
        n (terms in the Fourier series)
        &lt;input id="triangle-fourier-terms" class="fourier-plot-input" type="number" value="1" min="1" max="99" step="2"&gt;
      &lt;/label&gt;
    &lt;/div&gt;

    &lt;div id="triangle-fourier-message" class="fourier-plot-message"&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;div class="section" id="compact-formula-using-a-single-phase-shifted-sinusoid"&gt;
&lt;h2&gt;Compact formula using a single phase-shifted sinusoid&lt;/h2&gt;
&lt;p&gt;We’ve written the Fourier series for &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt; as follows so far:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/af4acda237be7c5348b05201f8f6f6c6b75e30b9.svg" style="height: 49px;" type="image/svg+xml"&gt;\[f(x)=\frac{a_0}{2}+\sum_{n=1}^{\infty}\left(a_n cos\frac{n\pi x}{L}+b_n sin\frac{n\pi x}{L}\right)\]&lt;/object&gt;
&lt;p&gt;We can rewrite this in a somewhat more compact form, using a single
sinusoid with a configurable phase at each &lt;img alt="n" class="valign-0" src="https://eli.thegreenplace.net/images/math/d1854cae891ec7b29161ccaf79a24b00c274bdaa.png" style="height: 8px;" /&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/0e0b529f628f772b5448c6ec96e6d5884885480b.svg" style="height: 49px;" type="image/svg+xml"&gt;\[f(x)=\frac{a_0}{2}+\sum_{n=1}^{\infty}q_n\cdot cos\left(\frac{n\pi x}{L}+\theta_n\right)\]&lt;/object&gt;
&lt;p&gt;Based on Appendix D, &lt;object class="valign-m4" data="https://eli.thegreenplace.net/images/math/0dacaea006b7f9f49020f85f9803b4467a77094b.svg" style="height: 12px;" type="image/svg+xml"&gt;q_n&lt;/object&gt; and &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/7dde5e64e049b615293b3dd8f94a70defa6a789b.svg" style="height: 15px;" type="image/svg+xml"&gt;\theta_n&lt;/object&gt; can be computed as
follows:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/dfca335ac97ffd88dc5a83a1ae48576dfa84d8b7.svg" style="height: 49px;" type="image/svg+xml"&gt;\[\begin{aligned}
    q_n&amp;amp;=\sqrt{a_n^2+b_n^2}\\
    \theta_n&amp;amp;=\operatorname{atan2}(-b_n,a_n)
\end{aligned}\]&lt;/object&gt;
&lt;p&gt;When Fourier series are used in the context of signal processing, this
formulation is easier to reason about because it represents the
magnitude and phase shift of each harmonic of &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt; in the
frequency domain &lt;a class="footnote-reference" href="#footnote-6" id="footnote-reference-6"&gt;[6]&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="complex-fourier-series"&gt;
&lt;h2&gt;Complex Fourier series&lt;/h2&gt;
&lt;p&gt;It should not come as a surprise that the Fourier series, being a
combination of trigonometric functions, can also be represented with
complex exponential functions.&lt;/p&gt;
&lt;p&gt;Specifically, we’ll show that our &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt; can be approximated as
follows:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/0330fae433a3379cfef316d192bfe32e38e0c9c9.svg" style="height: 48px;" type="image/svg+xml"&gt;\[f(x)=\sum_{n=-\infty}^{\infty}C_n\cdot e^{in\pi x/L}\]&lt;/object&gt;
&lt;p&gt;Let’s calculate &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/e6456786af3c999a8eda7658e37f7bf5d1bb0db0.svg" style="height: 15px;" type="image/svg+xml"&gt;C_n&lt;/object&gt;. We proceed in a manner similar to before,
by multiplying both sides of the equation by &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/e8594d0018e4f868e916c1657b8273bb9bfe2191.svg" style="height: 16px;" type="image/svg+xml"&gt;e^{-im\pi x/L}&lt;/object&gt; and
taking an integral in the range &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/5f1297a5a8758acc19fd4c325ae19878e7e2fbab.svg" style="height: 18px;" type="image/svg+xml"&gt;[-L,L]&lt;/object&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/f0a6b29c21ad20ebd950fe01f05b43c1fa67aa22.svg" style="height: 108px;" type="image/svg+xml"&gt;\[\begin{aligned}
\int_{-L}^{L}f(x)e^{-im\pi x/L}dx&amp;amp;=\sum_{n=-\infty}^{\infty}\int_{-L}^{L}C_n\cdot e^{in\pi x/L}e^{-im\pi x/L}dx\\
&amp;amp;=\sum_{n=-\infty}^{\infty}\int_{-L}^{L}C_n\cdot e^{i(n-m)\pi x/L}dx
\end{aligned}\]&lt;/object&gt;
&lt;p&gt;By Appendix A, the sum elements are all zero when &lt;object class="valign-m4" data="https://eli.thegreenplace.net/images/math/3ae4a06555822644174f9ff9419084466b3f7aa4.svg" style="height: 17px;" type="image/svg+xml"&gt;n\neq m&lt;/object&gt;. When
&lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/193bdae8b42385001cd707bc207b1d95bd6dc2b2.svg" style="height: 8px;" type="image/svg+xml"&gt;n=m&lt;/object&gt;, we get:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/e67fe1e7dabb9b96c06224e0d785d9a6d17601c4.svg" style="height: 44px;" type="image/svg+xml"&gt;\[\int_{-L}^{L}f(x)e^{-im\pi x/L}dx=\int_{-L}^{L}C_m\cdot 1 \cdot dx=2LC_m\]&lt;/object&gt;
&lt;p&gt;Therefore, renaming &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/6b0d31c0d563223024da45691584643ac78c96e8.svg" style="height: 8px;" type="image/svg+xml"&gt;m&lt;/object&gt; to &lt;img alt="n" class="valign-0" src="https://eli.thegreenplace.net/images/math/d1854cae891ec7b29161ccaf79a24b00c274bdaa.png" style="height: 8px;" /&gt; (since it’s just an arbitrary
integer constant):&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/d70fa4e75b4b604398d8d3c108a75bea622e72ef.svg" style="height: 44px;" type="image/svg+xml"&gt;\[C_n=\frac{1}{2L}\int_{-L}^{L}f(x)e^{-in\pi x/L}dx\]&lt;/object&gt;
&lt;p&gt;We’ve found an alternative formulation to Fourier series, using complex
exponentials instead of trigonometric functions. While this was a direct
derivation, another way to achieve the same result is to use the &lt;a class="reference external" href="https://eli.thegreenplace.net/2024/notes-on-the-euler-formula/"&gt;Euler
Formula&lt;/a&gt;
to derive:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/421172c9cfae4c087883b04c9d6c047b8c2a03e9.svg" style="height: 86px;" type="image/svg+xml"&gt;\[\begin{aligned}
    cos\theta&amp;amp;=\frac{e^{i\theta}+e^{-i\theta}}{2}\\
    sin\theta&amp;amp;=\frac{e^{i\theta}-e^{-i\theta}}{2i}
\end{aligned}\]&lt;/object&gt;
&lt;p&gt;And substitute these into the original Fourier series formula. I’ll
leave this as an exercise for the diligent reader; eventually, the
result will be the same. Moreover, it’s possible to show a direct
correspondence between &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/278ab95d3a54aae8eaa25c34af66d93a19b5e75f.svg" style="height: 11px;" type="image/svg+xml"&gt;a_n&lt;/object&gt;, &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/54d608cbef1529c3089b9281aa6dcb16a2e65f1f.svg" style="height: 15px;" type="image/svg+xml"&gt;b_n&lt;/object&gt; and &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/e6456786af3c999a8eda7658e37f7bf5d1bb0db0.svg" style="height: 15px;" type="image/svg+xml"&gt;C_n&lt;/object&gt;, for
&lt;object class="valign-m2" data="https://eli.thegreenplace.net/images/math/98b4f7b1f9e3613bfd676d3fdee9cd7b3b3f2a93.svg" style="height: 14px;" type="image/svg+xml"&gt;n&amp;gt;0&lt;/object&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/5f31bd78b45bb047f333a4257d8aa79cede582cd.svg" style="height: 118px;" type="image/svg+xml"&gt;\[\begin{aligned}
    C_0&amp;amp;=\frac{a_0}{2}\\
    C_n&amp;amp;=\frac{a_n-ib_n}{2}\\
    C_{-n}&amp;amp;=\frac{a_n+ib_n}{2}\\
\end{aligned}\]&lt;/object&gt;
&lt;p&gt;Note that &lt;object class="valign-m4" data="https://eli.thegreenplace.net/images/math/0bd1272b8272095400c72ab3f5bd52378bef592e.svg" style="height: 16px;" type="image/svg+xml"&gt;C_{-n}=C_n^*&lt;/object&gt; when both &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/278ab95d3a54aae8eaa25c34af66d93a19b5e75f.svg" style="height: 11px;" type="image/svg+xml"&gt;a_n&lt;/object&gt; and &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/54d608cbef1529c3089b9281aa6dcb16a2e65f1f.svg" style="height: 15px;" type="image/svg+xml"&gt;b_n&lt;/object&gt; are
real (which is the case for a real-valued &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt;). This helps
explain why the complex formulation has negative frequencies in the sum;
when the function is actually real, each negative frequency is paired up
with a positive frequency and the result is real &lt;a class="footnote-reference" href="#footnote-7" id="footnote-reference-7"&gt;[7]&lt;/a&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/530d1067a8b3fa34b5cc74abccb738a58540022a.svg" style="height: 112px;" type="image/svg+xml"&gt;\[\begin{aligned}
C_n e^{in\pi x/L}+C_{-n} e^{-in\pi x/L}&amp;amp;=C_n e^{in\pi x/L}+C_n^* e^{-in\pi x/L}\\
&amp;amp;=C_n e^{in\pi x/L}+\left(C_{n} e^{in\pi x/L}\right)^*\\
&amp;amp;=2\operatorname{Re}\bigg(C_{n} e^{in\pi x/L}\bigg)
\end{aligned}\]&lt;/object&gt;
&lt;p&gt;So, for a real function we only need to account for positive
frequencies:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/1e0faa05c55997d3b0f2c58c8bb16dca0b701fd0.svg" style="height: 49px;" type="image/svg+xml"&gt;\[f(x)=C_0+\sum_{n=1}^{\infty}2\operatorname{Re}\bigg(C_{n} e^{in\pi x/L}\bigg)\]&lt;/object&gt;
&lt;p&gt;We can take it further. &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/e6456786af3c999a8eda7658e37f7bf5d1bb0db0.svg" style="height: 15px;" type="image/svg+xml"&gt;C_n&lt;/object&gt; is a complex number, so let’s
represent it in polar form as &lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/1a21212523d9fce12a0769bdee7b76db92fd7e64.svg" style="height: 21px;" type="image/svg+xml"&gt;C_n=\frac{q_n}{2} e^{i\theta_n}&lt;/object&gt;
(the factor of half will make sense soon). Then:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/06c843ccf5621a53bd6a5c9cee599cfabbdc2a85.svg" style="height: 141px;" type="image/svg+xml"&gt;\[\begin{aligned}
\operatorname{Re}\bigg(C_{n} e^{in\pi x/L}\bigg)&amp;amp;=\operatorname{Re}\bigg(\frac{q_n}{2} e^{i\theta_n}e^{in\pi x/L}\bigg)\\
&amp;amp;=\frac{q_n}{2}\operatorname{Re}\bigg(e^{i(n\pi x/L + \theta_n)}\bigg)\\
&amp;amp;=\frac{q_n}{2} cos\bigg(\frac{n\pi x}{L}+\theta_n\bigg)
\end{aligned}\]&lt;/object&gt;
&lt;p&gt;And substituting back into the sum:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/c6d0bb12eb5cc2fbb7f881c0ecf8fcf0f700eeda.svg" style="height: 49px;" type="image/svg+xml"&gt;\[f(x)=C_0+\sum_{n=1}^{\infty}q_n cos\bigg(\frac{n\pi x}{L}+\theta_n\bigg)\]&lt;/object&gt;
&lt;p&gt;This is precisely the compact formulation from the previous section!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="fourier-orthogonal-basis-in-hilbert-space"&gt;
&lt;h2&gt;Fourier orthogonal basis in Hilbert space&lt;/h2&gt;
&lt;p&gt;The most beautiful aspect of Fourier theory is that it doesn’t just
happen to work by chance, and is deeply connected to linear algebra.
Please read &lt;a class="reference external" href="https://eli.thegreenplace.net/2025/hilbert-space-treating-functions-as-vectors/"&gt;my post on Hilbert
space&lt;/a&gt;
before proceeding.&lt;/p&gt;
&lt;p&gt;The space of real-valued square integrable functions &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/f1aae083af2c79348dd78712847ebd55537fa6e6.svg" style="height: 15px;" type="image/svg+xml"&gt;L^2&lt;/object&gt; forms a
Hilbert space, in which we can define the inner product (assuming real
functions):&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/979d2b712d89bac8c7950b7b13ecf0b895422989.svg" style="height: 44px;" type="image/svg+xml"&gt;\[\langle f,g \rangle=\int_{-L}^{L}f(x)g(x) dx\]&lt;/object&gt;
&lt;p&gt;We’ve demonstrated that the family of functions:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/f4bd9b77b937cf139ca1c071a6783fcdeafdd8cf.svg" style="height: 32px;" type="image/svg+xml"&gt;\[1,\qquad cos\frac{n\pi x}{L},\qquad sin\frac{n\pi x}{L}\]&lt;/object&gt;
&lt;p&gt;Are all mutually orthogonal, because their pairwise inner products are
zero! We’ve also shown that any function in &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/f1aae083af2c79348dd78712847ebd55537fa6e6.svg" style="height: 15px;" type="image/svg+xml"&gt;L^2&lt;/object&gt; can be
represented as a weighted sum of these functions:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/af4acda237be7c5348b05201f8f6f6c6b75e30b9.svg" style="height: 49px;" type="image/svg+xml"&gt;\[f(x)=\frac{a_0}{2}+\sum_{n=1}^{\infty}\left(a_n cos\frac{n\pi x}{L}+b_n sin\frac{n\pi x}{L}\right)\]&lt;/object&gt;
&lt;p&gt;So these functions form a &lt;em&gt;basis&lt;/em&gt; for &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/f1aae083af2c79348dd78712847ebd55537fa6e6.svg" style="height: 15px;" type="image/svg+xml"&gt;L^2&lt;/object&gt;. When we think of
these functions as vectors (in an infinite Hilbert space), much of what
we did in this post starts feeling like &amp;quot;normal&amp;quot; linear algebra. For
example, when we have a set of basis vectors and we want to know how to
represent some vector &lt;img alt="v" class="valign-0" src="https://eli.thegreenplace.net/images/math/7a38d8cbd20d9932ba948efaa364bb62651d5ad4.png" style="height: 8px;" /&gt; in this basis, we usually find the
coefficients by &lt;a class="reference external" href="https://eli.thegreenplace.net/2024/projections-and-projection-matrices/"&gt;projecting
it&lt;/a&gt;
onto the basis. E.g. with a basis vector &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/da8e9997a010be78b20108ce79fec1fb9c63d8dc.svg" style="height: 11px;" type="image/svg+xml"&gt;e_1&lt;/object&gt;, the coefficient of
&lt;img alt="v" class="valign-0" src="https://eli.thegreenplace.net/images/math/7a38d8cbd20d9932ba948efaa364bb62651d5ad4.png" style="height: 8px;" /&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/71b725179f33404ddc381f57d651c348ddfbee36.svg" style="height: 43px;" type="image/svg+xml"&gt;\[c=\frac{\langle v, e_1\rangle}{\langle e_1, e_1\rangle}\]&lt;/object&gt;
&lt;p&gt;Similarly, when we calculate the coefficient &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/54d608cbef1529c3089b9281aa6dcb16a2e65f1f.svg" style="height: 15px;" type="image/svg+xml"&gt;b_n&lt;/object&gt; for some
function &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt;, we project &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt; onto the basis vector
&lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/08cb570d61f7bb6f46cf3bf12e2c407a43e748b8.svg" style="height: 19px;" type="image/svg+xml"&gt;sin\frac{n\pi x}{L}&lt;/object&gt; by calculating:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/40fddd72747ac208870cfe3ee684128d21812cfa.svg" style="height: 45px;" type="image/svg+xml"&gt;\[b_n=\frac{\langle f(x), sin\frac{n\pi x}{L}\rangle}{\langle sin\frac{n\pi x}{L}, sin\frac{n\pi x}{L}\rangle}\]&lt;/object&gt;
&lt;p&gt;From Appendix B, we know that the denominator is &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/d160e0986aca4714714a16f29ec605af90be704d.svg" style="height: 12px;" type="image/svg+xml"&gt;L&lt;/object&gt;, and we’ve
just denoted:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/fc0911ff039b636dc0b1f9ae9cc804c68cab812d.svg" style="height: 44px;" type="image/svg+xml"&gt;\[\langle f(x), sin\frac{n\pi x}{L}\rangle=\int_{-L}^{L}f(x)sin\frac{n\pi x}{L}dx\]&lt;/object&gt;
&lt;p&gt;So we get:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/418eef7e6ac0ffd84fc9d87246f1329d17cb6d4c.svg" style="height: 44px;" type="image/svg+xml"&gt;\[b_n=\frac{1}{L}\int_{-L}^{L}f(x)sin\frac{n\pi x}{L}dx\]&lt;/object&gt;
&lt;p&gt;Which should look familiar!&lt;/p&gt;
&lt;p&gt;This is the core linear-algebra idea behind Fourier series: the
functions &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/356a192b7913b04c54574d18c28d46e6395428ab.svg" style="height: 12px;" type="image/svg+xml"&gt;1&lt;/object&gt;, &lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/0141773559b36de7a9e12feb64f961177df6b9d5.svg" style="height: 19px;" type="image/svg+xml"&gt;cos\frac{n\pi x}{L}&lt;/object&gt;, and
&lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/08cb570d61f7bb6f46cf3bf12e2c407a43e748b8.svg" style="height: 19px;" type="image/svg+xml"&gt;sin\frac{n\pi x}{L}&lt;/object&gt; play the role of orthogonal basis vectors,
while the Fourier coefficients are coordinates of &lt;img alt="f" class="valign-m4" src="https://eli.thegreenplace.net/images/math/4a0a19218e082a343a1b17e5333409af9d98f0f5.png" style="height: 16px;" /&gt; in this
basis. The integral formulas for &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/278ab95d3a54aae8eaa25c34af66d93a19b5e75f.svg" style="height: 11px;" type="image/svg+xml"&gt;a_n&lt;/object&gt; and &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/54d608cbef1529c3089b9281aa6dcb16a2e65f1f.svg" style="height: 15px;" type="image/svg+xml"&gt;b_n&lt;/object&gt; are not
mysterious tricks; they are projections, just like dot products with
basis vectors in ordinary Euclidean space.&lt;/p&gt;
&lt;p&gt;Fourier series therefore let us decompose a function into independent
orthogonal directions, much like decomposing a vector into its
&lt;img alt="x" class="valign-0" src="https://eli.thegreenplace.net/images/math/11f6ad8ec52a2984abaafd7c3b516503785c2072.png" style="height: 8px;" /&gt;, &lt;img alt="y" class="valign-m4" src="https://eli.thegreenplace.net/images/math/95cb0bfd2977c761298d9624e4b4d4c72a39974a.png" style="height: 12px;" /&gt;, and &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/395df8f7c51f007019cb30201c49e884b46b92fa.svg" style="height: 8px;" type="image/svg+xml"&gt;z&lt;/object&gt; components.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="appendix-a-integrals-of-sinusoids"&gt;
&lt;h2&gt;Appendix A: Integrals of sinusoids&lt;/h2&gt;
&lt;p&gt;For any integer &lt;object class="valign-m4" data="https://eli.thegreenplace.net/images/math/797c9948b82583001e2fe0e9343c783ca16709b0.svg" style="height: 17px;" type="image/svg+xml"&gt;n\neq 0&lt;/object&gt; and an arbitrary constant L, we have:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/582e940109a028f515a388f8003ebfbb00823dd3.svg" style="height: 92px;" type="image/svg+xml"&gt;\[\begin{aligned}
\int_{-L}^{L}cos\frac{n\pi x}{L}dx&amp;amp;=\bigg[\frac{L}{n\pi}sin\frac{n\pi x}{L}\bigg]_{-L}^{L}\\
&amp;amp;=\frac{L}{n\pi}(sin(n\pi)-sin(-n\pi))=0
\end{aligned}\]&lt;/object&gt;
&lt;p&gt;Similarly:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/f5220d0714489c00c6ec8c5ac2df22bf8d8cb1f4.svg" style="height: 92px;" type="image/svg+xml"&gt;\[\begin{aligned}
\int_{-L}^{L}sin\frac{n\pi x}{L}dx&amp;amp;=\bigg[\frac{-L}{n\pi}cos\frac{n\pi x}{L}\bigg]_{-L}^{L}\\
&amp;amp;=\frac{-L}{n\pi}\left(cos(n\pi)-cos(-n\pi)\right)=0
\end{aligned}\]&lt;/object&gt;
&lt;p&gt;Using these, we can calculate the integral of a complex exponential
function for an integer &lt;object class="valign-m4" data="https://eli.thegreenplace.net/images/math/797c9948b82583001e2fe0e9343c783ca16709b0.svg" style="height: 17px;" type="image/svg+xml"&gt;n\neq 0&lt;/object&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/3ae1815e617e0b0191a17fa9b07196ededfaf901.svg" style="height: 44px;" type="image/svg+xml"&gt;\[\begin{aligned}
\int_{-L}^{L}e^{in\pi x/L}dx=\int_{-L}^{L}\bigg[ cos\frac{n\pi x}{L} +i\cdot sin\frac{n\pi x}{L}\bigg] dx=0
\end{aligned}\]&lt;/object&gt;
&lt;/div&gt;
&lt;div class="section" id="appendix-b-integrals-of-products-of-sinusoids"&gt;
&lt;h2&gt;Appendix B: Integrals of products of sinusoids&lt;/h2&gt;
&lt;p&gt;We’ll start with the product of two sines, for any positive integers
&lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/6b0d31c0d563223024da45691584643ac78c96e8.svg" style="height: 8px;" type="image/svg+xml"&gt;m&lt;/object&gt; and &lt;img alt="n" class="valign-0" src="https://eli.thegreenplace.net/images/math/d1854cae891ec7b29161ccaf79a24b00c274bdaa.png" style="height: 8px;" /&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/e95f65d1c2626cffbdb8e474676708ef6ca38278.svg" style="height: 44px;" type="image/svg+xml"&gt;\[ss=\int_{-L}^{L}sin\frac{m\pi x}{L}\cdot sin\frac{n\pi x}{L}dx\]&lt;/object&gt;
&lt;p&gt;Using the trigonometric identity for a product of sines, we can write:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/06a26e4d08d086b4e29b1c23a918009d9f03886c.svg" style="height: 96px;" type="image/svg+xml"&gt;\[\begin{aligned}
    ss&amp;amp;=\frac{1}{2}\int_{-L}^{L}\bigg(cos\frac{(m-n)\pi x}{L}-cos\frac{(m+n)\pi x}{L}\bigg)dx\\
    &amp;amp;=\frac{1}{2}\int_{-L}^{L}cos\frac{(m-n)\pi x}{L}dx-\frac{1}{2}\int_{-L}^{L}cos\frac{(m+n)\pi x}{L}dx
\end{aligned}\]&lt;/object&gt;
&lt;p&gt;Now let’s focus on two different scenarios, &lt;object class="valign-m4" data="https://eli.thegreenplace.net/images/math/c21c6fe891018692f0717e8f38fbd44941a8ab7c.svg" style="height: 17px;" type="image/svg+xml"&gt;m\neq n&lt;/object&gt; and
&lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/49cfc75f5c423bc73e28d59b794e2815d1f4bb54.svg" style="height: 8px;" type="image/svg+xml"&gt;m=n&lt;/object&gt;.&lt;/p&gt;
&lt;p&gt;If &lt;object class="valign-m4" data="https://eli.thegreenplace.net/images/math/c21c6fe891018692f0717e8f38fbd44941a8ab7c.svg" style="height: 17px;" type="image/svg+xml"&gt;m\neq n&lt;/object&gt;, then each of the integrals constituting &lt;em&gt;ss&lt;/em&gt; are 0
(see on Appendix A), so &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/19db77825a7cd28d0344f9b160251f8230d08b30.svg" style="height: 12px;" type="image/svg+xml"&gt;ss=0&lt;/object&gt;.&lt;/p&gt;
&lt;p&gt;If &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/49cfc75f5c423bc73e28d59b794e2815d1f4bb54.svg" style="height: 8px;" type="image/svg+xml"&gt;m=n&lt;/object&gt;, then the second integral is still 0, but the first one
isn’t:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/024afeb4e1aa17cd4cf9bfc70fdf0d9d09be7faf.svg" style="height: 96px;" type="image/svg+xml"&gt;\[\begin{aligned}
    ss&amp;amp;=\frac{1}{2}\int_{-L}^{L}cos\frac{0\pi x}{L}dx\\
    &amp;amp;=\frac{1}{2}\int_{-L}^{L}1dx=L
\end{aligned}\]&lt;/object&gt;
&lt;p&gt;Therefore:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/85dbc4cbb84044fdfda7f2cd5f42453581bc987f.svg" style="height: 54px;" type="image/svg+xml"&gt;\[ss=\int_{-L}^{L}sin\frac{m\pi x}{L}\cdot sin\frac{n\pi x}{L}dx=
\begin{cases}
    L      &amp;amp; m = n \\
    0      &amp;amp; m \neq n
\end{cases}\]&lt;/object&gt;
&lt;p&gt;We can use exactly the same approach to show that:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/bd73561c5868f1519b9b264f81010d9a513dbae6.svg" style="height: 54px;" type="image/svg+xml"&gt;\[cc=\int_{-L}^{L}cos\frac{m\pi x}{L}\cdot cos\frac{n\pi x}{L}dx=
\begin{cases}
    L      &amp;amp; m = n \\
    0      &amp;amp; m \neq n
\end{cases}\]&lt;/object&gt;
&lt;p&gt;One more variant to cover:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/f6b7bc73da6ce073e32f0f1253ab5270d19973c5.svg" style="height: 44px;" type="image/svg+xml"&gt;\[sc=\int_{-L}^{L}sin\frac{m\pi x}{L}\cdot cos\frac{n\pi x}{L}dx\]&lt;/object&gt;
&lt;p&gt;Since sine is an odd function and cosine is an even function, their
product is an odd function. And the integral of an odd function over a
symmetric interval is 0 (see &lt;a class="reference external" href="https://eli.thegreenplace.net/2025/notes-on-even-and-odd-functions/"&gt;this post for more
details&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Therefore:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/8b8669232a6f30dc68a091abffc787858632cb5c.svg" style="height: 44px;" type="image/svg+xml"&gt;\[sc=\int_{-L}^{L}sin\frac{m\pi x}{L}\cdot cos\frac{n\pi x}{L}dx=0\]&lt;/object&gt;
&lt;/div&gt;
&lt;div class="section" id="appendix-c-a-useful-integral"&gt;
&lt;h2&gt;Appendix C: A useful integral&lt;/h2&gt;
&lt;p&gt;Let’s calculate the indefinite integral:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/df863382117243993f6ef34cf6216f181f891d19.svg" style="height: 40px;" type="image/svg+xml"&gt;\[I=\int x\cdot sin(kx) dx\]&lt;/object&gt;
&lt;p&gt;For some constant &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/13fbd79c3d390e5d6585a21e11ff5ec1970cff0c.svg" style="height: 12px;" type="image/svg+xml"&gt;k&lt;/object&gt;. We’ll use integration by parts:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/8033553504b7b9ace816e595172d3942155fa8f1.svg" style="height: 40px;" type="image/svg+xml"&gt;\[\int u\cdot dv =u\cdot v - \int v\cdot du\]&lt;/object&gt;
&lt;p&gt;Here &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/a6327890f9dd75f11f057eb1e4465c365cd3b531.svg" style="height: 8px;" type="image/svg+xml"&gt;u=x&lt;/object&gt;, so &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/febc1f31b2dbfc3e5d91801704e4d37f73c047af.svg" style="height: 12px;" type="image/svg+xml"&gt;du=dx&lt;/object&gt;. Also &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/0a3c6cb92a28005f4435cf19ab35bd121d69cd09.svg" style="height: 19px;" type="image/svg+xml"&gt;dv=sin(kx)&lt;/object&gt;, so
&lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/850143056c00b486079d9e102e7354b38236fbcb.svg" style="height: 25px;" type="image/svg+xml"&gt;v=-\frac{cos(kx)}{k}&lt;/object&gt;.&lt;/p&gt;
&lt;p&gt;Putting it together:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/552c213e2d0c70d208bd577d07d4fb37f2d19a0c.svg" style="height: 42px;" type="image/svg+xml"&gt;\[I=\frac{-x\cdot cos(kx)}{k}+\int \frac{cos(kx)}{k} dx=\frac{-x\cdot cos(kx)}{k}+\frac{sin(kx)}{k^2}\]&lt;/object&gt;
&lt;/div&gt;
&lt;div class="section" id="appendix-d-sinusoid-with-phase-as-a-sum-of-sin-and-cos"&gt;
&lt;h2&gt;Appendix D: Sinusoid with phase as a sum of sin and cos&lt;/h2&gt;
&lt;p&gt;Let’s take a general sinusoid with magnitude &lt;object class="valign-m4" data="https://eli.thegreenplace.net/images/math/22ea1c649c82946aa6e479e1ffd321e4a318b1b0.svg" style="height: 12px;" type="image/svg+xml"&gt;q&lt;/object&gt;, frequency
&lt;img alt="w" class="valign-0" src="https://eli.thegreenplace.net/images/math/aff024fe4ab0fece4091de044c58c9ae4233383a.png" style="height: 8px;" /&gt; and phase &lt;img alt="\theta" class="valign-0" src="https://eli.thegreenplace.net/images/math/cb005d76f9f2e394a770c2562c2e150a413b3216.png" style="height: 12px;" /&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/2c7bd5bad4fe6202ce4cdb40050bd68190b3aeb9.svg" style="height: 19px;" type="image/svg+xml"&gt;\[s(x)=q\cdot cos(wx+\theta)\]&lt;/object&gt;
&lt;p&gt;We’re going to show that &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/68cee1190d7058555e058756fed1d6527ab89855.svg" style="height: 19px;" type="image/svg+xml"&gt;s(x)&lt;/object&gt; can be represented as a sum of a
&lt;em&gt;sine&lt;/em&gt; and a &lt;em&gt;cosine&lt;/em&gt; with no phase. This is related to &lt;a class="reference external" href="https://eli.thegreenplace.net/2023/sum-of-same-frequency-sinusoids/"&gt;my earlier post
on the sum of same-frequency
sinusoids&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let’s start by expanding &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/68cee1190d7058555e058756fed1d6527ab89855.svg" style="height: 19px;" type="image/svg+xml"&gt;s(x)&lt;/object&gt; using a trigonometric identity:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/e8984abab15f66276ebb92fa0d02cfe9eed5c934.svg" style="height: 19px;" type="image/svg+xml"&gt;\[s(x)=q\cdot cos(\theta)cos(wx)-q\cdot sin(\theta)sin(wx)\]&lt;/object&gt;
&lt;p&gt;Now we’ll denote: &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/dcb99eb292280e6a796bbcb514744c3c301f0a35.svg" style="height: 19px;" type="image/svg+xml"&gt;a=q\cdot cos(\theta)&lt;/object&gt; and
&lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/af9b66bdf49b9575a347a5bf3bb7acfad1ef390b.svg" style="height: 19px;" type="image/svg+xml"&gt;b=-q\cdot sin(\theta)&lt;/object&gt;, so:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/fac6a492043d2c4afd90bf19471756cd0177227e.svg" style="height: 19px;" type="image/svg+xml"&gt;\[s(x)=a\cdot cos(wx)+b\cdot sin(wx)\]&lt;/object&gt;
&lt;p&gt;We have &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/86f7e437faa5a7fce15d1ddcb9eaeaea377667b8.svg" style="height: 8px;" type="image/svg+xml"&gt;a&lt;/object&gt; and &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98.svg" style="height: 13px;" type="image/svg+xml"&gt;b&lt;/object&gt; in terms of &lt;object class="valign-m4" data="https://eli.thegreenplace.net/images/math/22ea1c649c82946aa6e479e1ffd321e4a318b1b0.svg" style="height: 12px;" type="image/svg+xml"&gt;q&lt;/object&gt; and
&lt;img alt="\theta" class="valign-0" src="https://eli.thegreenplace.net/images/math/cb005d76f9f2e394a770c2562c2e150a413b3216.png" style="height: 12px;" /&gt;, but what about the other way around?&lt;/p&gt;
&lt;p&gt;Let’s take the equations:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/bed3f7ea8728473d264871af14bedc7371943db4.svg" style="height: 46px;" type="image/svg+xml"&gt;\[\begin{aligned}
    a&amp;amp;=q\cdot cos(\theta)\\
    b&amp;amp;=-q\cdot sin(\theta)
\end{aligned}\]&lt;/object&gt;
&lt;p&gt;Square both of them and add together:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/ef69aacd4b99f916e773666322d7664a7f80b416.svg" style="height: 52px;" type="image/svg+xml"&gt;\[\begin{aligned}
    a^2+b^2&amp;amp;=q^2\cdot(cos^2(\theta)+sin^2(\theta))=q^2\\
    &amp;amp;\Rightarrow q=\sqrt{a^2+b^2}
\end{aligned}\]&lt;/object&gt;
&lt;p&gt;Now we’ll take the equations for &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98.svg" style="height: 13px;" type="image/svg+xml"&gt;b&lt;/object&gt; and &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/86f7e437faa5a7fce15d1ddcb9eaeaea377667b8.svg" style="height: 8px;" type="image/svg+xml"&gt;a&lt;/object&gt; and divide one
by the other:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/df7db0eac6bb842fe0fd6d76e1cd918a74fce9bb.svg" style="height: 69px;" type="image/svg+xml"&gt;\[\begin{aligned}
    \frac{b}{a}&amp;amp;=\frac{-sin(\theta)}{cos(\theta)}\\
    &amp;amp;\Rightarrow\theta=\operatorname{atan2}(-b,a)
\end{aligned}\]&lt;/object&gt;
&lt;p&gt;Where &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Atan2"&gt;the atan2 function&lt;/a&gt; is
careful to take into account the sign of both numerator and denominator.
Also it’s worth mentioning that &lt;img alt="\theta" class="valign-0" src="https://eli.thegreenplace.net/images/math/cb005d76f9f2e394a770c2562c2e150a413b3216.png" style="height: 12px;" /&gt; is determined up to
additions of &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/0833718ca4569f36e84dbdc7742eaec65e49b150.svg" style="height: 12px;" type="image/svg+xml"&gt;2\pi&lt;/object&gt;.&lt;/p&gt;
&lt;p&gt;To conclude, for any &lt;object class="valign-m4" data="https://eli.thegreenplace.net/images/math/22ea1c649c82946aa6e479e1ffd321e4a318b1b0.svg" style="height: 12px;" type="image/svg+xml"&gt;q&lt;/object&gt;, &lt;img alt="w" class="valign-0" src="https://eli.thegreenplace.net/images/math/aff024fe4ab0fece4091de044c58c9ae4233383a.png" style="height: 8px;" /&gt; and &lt;img alt="\theta" class="valign-0" src="https://eli.thegreenplace.net/images/math/cb005d76f9f2e394a770c2562c2e150a413b3216.png" style="height: 12px;" /&gt;:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/f202b285532ca490fbec00a7a182a3ee518d945f.svg" style="height: 19px;" type="image/svg+xml"&gt;\[q\cdot cos(wx+\theta)=a\cdot cos(wx)+b\cdot sin(wx)\]&lt;/object&gt;
&lt;p&gt;With the aforementioned conversion formulas for &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/86f7e437faa5a7fce15d1ddcb9eaeaea377667b8.svg" style="height: 8px;" type="image/svg+xml"&gt;a&lt;/object&gt;, &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98.svg" style="height: 13px;" type="image/svg+xml"&gt;b&lt;/object&gt;.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;A function &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt; is called periodic if there exists some
constant &lt;object class="valign-m2" data="https://eli.thegreenplace.net/images/math/cf2af45cefa3d7ceadaff0dfa38db6898e6ff301.svg" style="height: 14px;" type="image/svg+xml"&gt;P&amp;gt;0&lt;/object&gt; such that &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/02c74bd56236fce328ac0a48fda293dca178af78.svg" style="height: 19px;" type="image/svg+xml"&gt;f(x+P)=f(x)\ \forall x&lt;/object&gt;. In our
case, we denote the period as &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/f200dcf24d7a0a3035a4c7023b03459f6b22e3cc.svg" style="height: 12px;" type="image/svg+xml"&gt;P=2L&lt;/object&gt; to make presentation and
calculations neater.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Since &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/9ea1a3c68ad2e5a68e78cff7fe65bf38d08519bb.svg" style="height: 20px;" type="image/svg+xml"&gt;f(x)\in L^2&lt;/object&gt;, we know that it’s integrable on a finite
interval&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;There’s also an infinite number of less natural ways to extend the
function; they will still work, but may make the calculation
needlessly complicated&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-4" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-4"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;I chose this one because the more common ones like a square wave and
a sawtooth wave have many derivations available online, including on
Wikipedia.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-5" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-5"&gt;[5]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Try it for yourself - it’s a good exercise.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-6" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-6"&gt;[6]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;When &lt;object class="valign-m4" data="https://eli.thegreenplace.net/images/math/7cf469bc52df59c26bf99dd9ecbf71b7f6f5ab78.svg" style="height: 16px;" type="image/svg+xml"&gt;q_n=0&lt;/object&gt; for some &lt;img alt="n" class="valign-0" src="https://eli.thegreenplace.net/images/math/d1854cae891ec7b29161ccaf79a24b00c274bdaa.png" style="height: 8px;" /&gt;, the corresponding
&lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/7dde5e64e049b615293b3dd8f94a70defa6a789b.svg" style="height: 15px;" type="image/svg+xml"&gt;\theta_n&lt;/object&gt; can be chosen arbitrarily.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-7" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-7"&gt;[7]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;For a complex-valued &lt;img alt="f(x)" class="valign-m4" src="https://eli.thegreenplace.net/images/math/3e03f4706048fbc6c5a252a85d066adf107fcc1f.png" style="height: 18px;" /&gt;, this conjugate symmetry no longer
holds in general. &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/278ab95d3a54aae8eaa25c34af66d93a19b5e75f.svg" style="height: 11px;" type="image/svg+xml"&gt;a_n&lt;/object&gt; and &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/54d608cbef1529c3089b9281aa6dcb16a2e65f1f.svg" style="height: 15px;" type="image/svg+xml"&gt;b_n&lt;/object&gt; may be complex, so
&lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/e6456786af3c999a8eda7658e37f7bf5d1bb0db0.svg" style="height: 15px;" type="image/svg+xml"&gt;C_n&lt;/object&gt; and &lt;object class="valign-m3" data="https://eli.thegreenplace.net/images/math/bdd14e4abf9623d7582bf4f5ba37adc1b564491f.svg" style="height: 15px;" type="image/svg+xml"&gt;C_{-n}&lt;/object&gt; need not be conjugates.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;script src="https://eli.thegreenplace.net/demos/fourier/fourier-plot.js"&gt;
&lt;/script&gt;&lt;/div&gt;
</content><category term="misc"></category><category term="Math"></category></entry><entry><title>Scaling, stretching and shifting sinusoids</title><link href="https://eli.thegreenplace.net/2026/scaling-stretching-and-shifting-sinusoids/" rel="alternate"></link><published>2026-05-02T07:17:00-07:00</published><updated>2026-05-02T14:18:12-07:00</updated><author><name>Eli Bendersky</name></author><id>tag:eli.thegreenplace.net,2026-05-02:/2026/scaling-stretching-and-shifting-sinusoids/</id><summary type="html">&lt;p&gt;This is a brief and simple &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt; explanation of how to adjust the
standard sinusoid &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/1f2ea8ffab8deb0e5b854a260a68b42b7eb7b048.svg" style="height: 19px;" type="image/svg+xml"&gt;sin(x)&lt;/object&gt; to change its amplitude, frequency and
phase shift. More precisely, given the general function:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/85a45cfe4c1671765c3ab7e3949d8f74f780a0f3.svg" style="height: 19px;" type="image/svg+xml"&gt;\[s(x)=A\cdot sin(w\cdot x+\theta)\]&lt;/object&gt;
&lt;p&gt;We’ll see how adjusting the parameters &lt;img alt="A" class="valign-0" src="https://eli.thegreenplace.net/images/math/6dcd4ce23d88e2ee9568ba546c007c63d9131c1b.png" style="height: 12px;" /&gt;, &lt;img alt="w" class="valign-0" src="https://eli.thegreenplace.net/images/math/aff024fe4ab0fece4091de044c58c9ae4233383a.png" style="height: 8px;" /&gt; and
&lt;img alt="\theta" class="valign-0" src="https://eli.thegreenplace.net/images/math/cb005d76f9f2e394a770c2562c2e150a413b3216.png" style="height: 12px;" /&gt; affect the …&lt;/p&gt;</summary><content type="html">&lt;p&gt;This is a brief and simple &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt; explanation of how to adjust the
standard sinusoid &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/1f2ea8ffab8deb0e5b854a260a68b42b7eb7b048.svg" style="height: 19px;" type="image/svg+xml"&gt;sin(x)&lt;/object&gt; to change its amplitude, frequency and
phase shift. More precisely, given the general function:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/85a45cfe4c1671765c3ab7e3949d8f74f780a0f3.svg" style="height: 19px;" type="image/svg+xml"&gt;\[s(x)=A\cdot sin(w\cdot x+\theta)\]&lt;/object&gt;
&lt;p&gt;We’ll see how adjusting the parameters &lt;img alt="A" class="valign-0" src="https://eli.thegreenplace.net/images/math/6dcd4ce23d88e2ee9568ba546c007c63d9131c1b.png" style="height: 12px;" /&gt;, &lt;img alt="w" class="valign-0" src="https://eli.thegreenplace.net/images/math/aff024fe4ab0fece4091de044c58c9ae4233383a.png" style="height: 8px;" /&gt; and
&lt;img alt="\theta" class="valign-0" src="https://eli.thegreenplace.net/images/math/cb005d76f9f2e394a770c2562c2e150a413b3216.png" style="height: 12px;" /&gt; affect the shape of &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/68cee1190d7058555e058756fed1d6527ab89855.svg" style="height: 19px;" type="image/svg+xml"&gt;s(x)&lt;/object&gt;. Each section below
covers one of these aspects mathematically, and you can use the demo at
the bottom to experiment with the topic visually.&lt;/p&gt;
&lt;div class="section" id="scaling"&gt;
&lt;h2&gt;Scaling&lt;/h2&gt;
&lt;p&gt;Scaling is conceptually the simplest change; we adjust &lt;img alt="A" class="valign-0" src="https://eli.thegreenplace.net/images/math/6dcd4ce23d88e2ee9568ba546c007c63d9131c1b.png" style="height: 12px;" /&gt; to
increase or decrease the amplitude (maximal height) of &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/68cee1190d7058555e058756fed1d6527ab89855.svg" style="height: 19px;" type="image/svg+xml"&gt;s(x)&lt;/object&gt;. Setting
&lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/6a6272d9558de919b19aa13d56dc37e11c358150.svg" style="height: 13px;" type="image/svg+xml"&gt;A=2&lt;/object&gt; will make the &lt;img alt="y" class="valign-m4" src="https://eli.thegreenplace.net/images/math/95cb0bfd2977c761298d9624e4b4d4c72a39974a.png" style="height: 12px;" /&gt; value twice as large (in both the positive
and negative direction) as the original function.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="stretching"&gt;
&lt;h2&gt;Stretching&lt;/h2&gt;
&lt;p&gt;Stretching changes the frequency of &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/1f2ea8ffab8deb0e5b854a260a68b42b7eb7b048.svg" style="height: 19px;" type="image/svg+xml"&gt;sin(x)&lt;/object&gt;, which is inverse
proportional to its period. The baseline function &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/1f2ea8ffab8deb0e5b854a260a68b42b7eb7b048.svg" style="height: 19px;" type="image/svg+xml"&gt;sin(x)&lt;/object&gt; has a
period of &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/0833718ca4569f36e84dbdc7742eaec65e49b150.svg" style="height: 12px;" type="image/svg+xml"&gt;2\pi&lt;/object&gt;, meaning it repeats every &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/0833718ca4569f36e84dbdc7742eaec65e49b150.svg" style="height: 12px;" type="image/svg+xml"&gt;2\pi&lt;/object&gt;. In other
words, &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/48691bfc145b339ef1a94cfac6b43af86264c14b.svg" style="height: 19px;" type="image/svg+xml"&gt;sin(x)=sin(x+2\pi)&lt;/object&gt; for any &lt;img alt="x" class="valign-0" src="https://eli.thegreenplace.net/images/math/11f6ad8ec52a2984abaafd7c3b516503785c2072.png" style="height: 8px;" /&gt;.&lt;/p&gt;
&lt;p&gt;If we set &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/c8cb42808e0de9f61517078296f19134ac680422.svg" style="height: 12px;" type="image/svg+xml"&gt;w=2&lt;/object&gt;, we get &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/fd197962a5779ba863ea0db91d1ee9210d07002a.svg" style="height: 19px;" type="image/svg+xml"&gt;sin(2x)&lt;/object&gt;. This function repeats
itself twice as fast as &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/1f2ea8ffab8deb0e5b854a260a68b42b7eb7b048.svg" style="height: 19px;" type="image/svg+xml"&gt;sin(x)&lt;/object&gt;, because &lt;img alt="x" class="valign-0" src="https://eli.thegreenplace.net/images/math/11f6ad8ec52a2984abaafd7c3b516503785c2072.png" style="height: 8px;" /&gt; is multiplied
by 2 before being fed into the sinusoid. If &lt;img alt="x" class="valign-0" src="https://eli.thegreenplace.net/images/math/11f6ad8ec52a2984abaafd7c3b516503785c2072.png" style="height: 8px;" /&gt; changes by
&lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/6ac47b6d7372b4087583cfd048d20f4c1571f5cf.svg" style="height: 8px;" type="image/svg+xml"&gt;\pi&lt;/object&gt;, the sinusoid’s input changes by &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/0833718ca4569f36e84dbdc7742eaec65e49b150.svg" style="height: 12px;" type="image/svg+xml"&gt;2\pi&lt;/object&gt;.
Therefore, the period of &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/fd197962a5779ba863ea0db91d1ee9210d07002a.svg" style="height: 19px;" type="image/svg+xml"&gt;sin(2x)&lt;/object&gt; is &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/6ac47b6d7372b4087583cfd048d20f4c1571f5cf.svg" style="height: 8px;" type="image/svg+xml"&gt;\pi&lt;/object&gt;, the period of
&lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/fd718cb7cfa198e46efa9f40dda81c2fb2aa26c9.svg" style="height: 19px;" type="image/svg+xml"&gt;sin(4x)&lt;/object&gt; is &lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/7813100388622a69c767d297b453832a9df166fb.svg" style="height: 19px;" type="image/svg+xml"&gt;\frac{\pi}{2}&lt;/object&gt; and so on. &lt;a class="footnote-reference" href="#footnote-2" id="footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;More generally, the period of &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/59349af0e0016059129f0961133daaa009e68aa1.svg" style="height: 19px;" type="image/svg+xml"&gt;sin(wx)&lt;/object&gt; is &lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/af5a5e1f3222c095bbceea6c7cf5e6685bdc0ce5.svg" style="height: 22px;" type="image/svg+xml"&gt;\frac{2\pi}{w}&lt;/object&gt;.
Play with the demo below to see this in action, by changing &lt;img alt="w" class="valign-0" src="https://eli.thegreenplace.net/images/math/aff024fe4ab0fece4091de044c58c9ae4233383a.png" style="height: 8px;" /&gt;
and observing how the waveform changes.&lt;/p&gt;
&lt;p&gt;If we know the period &lt;object class="valign-m4" data="https://eli.thegreenplace.net/images/math/516b9783fca517eecbd1d064da2d165310b19759.svg" style="height: 12px;" type="image/svg+xml"&gt;p&lt;/object&gt; we want, we can easily calculate the
&lt;img alt="w" class="valign-0" src="https://eli.thegreenplace.net/images/math/aff024fe4ab0fece4091de044c58c9ae4233383a.png" style="height: 8px;" /&gt; that gives us this period:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/1e0b7e1350f41a5d194ed4596e24ba19298728ff.svg" style="height: 40px;" type="image/svg+xml"&gt;\[p=\frac{2\pi}{w} \implies w=\frac{2\pi}{p}\]&lt;/object&gt;
&lt;/div&gt;
&lt;div class="section" id="shifting"&gt;
&lt;h2&gt;Shifting&lt;/h2&gt;
&lt;p&gt;The final parameter we discuss is &lt;img alt="\theta" class="valign-0" src="https://eli.thegreenplace.net/images/math/cb005d76f9f2e394a770c2562c2e150a413b3216.png" style="height: 12px;" /&gt;; it’s called the
&lt;em&gt;phase&lt;/em&gt; of the sinusoid. In the baseline &lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/1f2ea8ffab8deb0e5b854a260a68b42b7eb7b048.svg" style="height: 19px;" type="image/svg+xml"&gt;sin(x)&lt;/object&gt;,
&lt;img alt="\theta=0" class="valign-0" src="https://eli.thegreenplace.net/images/math/a1dffbe89f1ec5a919198de979fca459eb7fdf84.png" style="height: 12px;" /&gt;. The sinusoid is 0 at &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/8bdf21367eee06097384c37b0448375f07f950f0.svg" style="height: 12px;" type="image/svg+xml"&gt;x=0&lt;/object&gt;, achieves its
positive peak at &lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/45cd304b3c843af35927b7c0245ea384eb910e58.svg" style="height: 19px;" type="image/svg+xml"&gt;x=\frac{\pi}{2}&lt;/object&gt;, crosses 0 again at
&lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/772d339297416ff00b9b1bb9e94d4967ed2cd240.svg" style="height: 8px;" type="image/svg+xml"&gt;x=\pi&lt;/object&gt;, negative peak at &lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/93bdc56d302f94f04eabe5a5613f247ae868be72.svg" style="height: 22px;" type="image/svg+xml"&gt;x=\frac{3\pi}{2}&lt;/object&gt; and returns to
its original position at &lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/c40e087d3c6af21cd2ddb732737bcab6381f1feb.svg" style="height: 12px;" type="image/svg+xml"&gt;x=2\pi&lt;/object&gt; where the repetition begins.&lt;/p&gt;
&lt;p&gt;By adding a non-zero &lt;img alt="\theta" class="valign-0" src="https://eli.thegreenplace.net/images/math/cb005d76f9f2e394a770c2562c2e150a413b3216.png" style="height: 12px;" /&gt;, we don’t affect the sinusoid’s
amplitude or frequency, but we do shift it right or left along the
&lt;img alt="x" class="valign-0" src="https://eli.thegreenplace.net/images/math/11f6ad8ec52a2984abaafd7c3b516503785c2072.png" style="height: 8px;" /&gt; axis. For example, suppose we use the function
&lt;object class="valign-m5" data="https://eli.thegreenplace.net/images/math/f07cc6da4bbe7259442b8c4dc6f9bdaaa2c2933c.svg" style="height: 19px;" type="image/svg+xml"&gt;sin(x+\theta)&lt;/object&gt; with &lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/0debc99bd20cf70823ed08246f837595d0cde3bf.svg" style="height: 19px;" type="image/svg+xml"&gt;\theta=\frac{\pi}{2}&lt;/object&gt;. Then when
&lt;object class="valign-0" data="https://eli.thegreenplace.net/images/math/8bdf21367eee06097384c37b0448375f07f950f0.svg" style="height: 12px;" type="image/svg+xml"&gt;x=0&lt;/object&gt;, we have &lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/f04381be541e525ad448580297568f9bf94dbc5d.svg" style="height: 20px;" type="image/svg+xml"&gt;sin(\frac{\pi}{2})&lt;/object&gt;, so the sinusoid is
already at its positive peak; at &lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/45cd304b3c843af35927b7c0245ea384eb910e58.svg" style="height: 19px;" type="image/svg+xml"&gt;x=\frac{\pi}{2}&lt;/object&gt;, the sinusoid
crosses 0 into the negatives, etc. Everything happens earlier (by
exactly the value of &lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/0debc99bd20cf70823ed08246f837595d0cde3bf.svg" style="height: 19px;" type="image/svg+xml"&gt;\theta=\frac{\pi}{2}&lt;/object&gt;) than in the baseline
sinusoid. In other words, we’ve shifted the function &lt;em&gt;left&lt;/em&gt; by
&lt;object class="valign-m6" data="https://eli.thegreenplace.net/images/math/7813100388622a69c767d297b453832a9df166fb.svg" style="height: 19px;" type="image/svg+xml"&gt;\frac{\pi}{2}&lt;/object&gt;. Similarly, when &lt;img alt="\theta" class="valign-0" src="https://eli.thegreenplace.net/images/math/cb005d76f9f2e394a770c2562c2e150a413b3216.png" style="height: 12px;" /&gt; is negative,
everything happens later, and the function is shifted &lt;em&gt;right&lt;/em&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="putting-it-all-together"&gt;
&lt;h2&gt;Putting it all together&lt;/h2&gt;
&lt;p&gt;We’ve now gone over all the parameters for the function:&lt;/p&gt;
&lt;object class="align-center" data="https://eli.thegreenplace.net/images/math/85a45cfe4c1671765c3ab7e3949d8f74f780a0f3.svg" style="height: 19px;" type="image/svg+xml"&gt;\[s(x)=A\cdot sin(w\cdot x+\theta)\]&lt;/object&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;img alt="A" class="valign-0" src="https://eli.thegreenplace.net/images/math/6dcd4ce23d88e2ee9568ba546c007c63d9131c1b.png" style="height: 12px;" /&gt; controls the scaling factor (amplitude).&lt;/li&gt;
&lt;li&gt;&lt;img alt="w" class="valign-0" src="https://eli.thegreenplace.net/images/math/aff024fe4ab0fece4091de044c58c9ae4233383a.png" style="height: 8px;" /&gt; is the frequency and controls the repetition period&lt;/li&gt;
&lt;li&gt;&lt;img alt="\theta" class="valign-0" src="https://eli.thegreenplace.net/images/math/cb005d76f9f2e394a770c2562c2e150a413b3216.png" style="height: 12px;" /&gt; controls the phase - how much the sinusoid is shifted
left or right&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use the demo below to adjust these parameters and observe their effect on
the sinusoid:&lt;/p&gt;
&lt;link rel="stylesheet" href="https://eli.thegreenplace.net/demos/sinus/sinus-demo.css"&gt;

&lt;!-- Canvas shows the current graph and legend;
     controls below provide the parameter inputs. --&gt;
&lt;div class="sinus-demo-wrap"&gt;
  &lt;canvas id="sinus-demo-canvas" class="sinus-demo-canvas" width="760" height="390"&gt;
  Your browser does not support the HTML5 canvas tag.
  &lt;/canvas&gt;

  &lt;div class="sinus-demo-controls"&gt;
    &lt;div class="sinus-demo-grid"&gt;
      &lt;label class="sinus-demo-label" for="sinus-demo-amp"&gt;
        A
        &lt;input id="sinus-demo-amp" class="sinus-demo-input" type="number" value="1" step="0.1"&gt;
      &lt;/label&gt;

      &lt;label class="sinus-demo-label" for="sinus-demo-omega"&gt;
        ω
        &lt;input id="sinus-demo-omega" class="sinus-demo-input" type="number" value="1" step="0.1"&gt;
      &lt;/label&gt;

      &lt;label class="sinus-demo-label" for="sinus-demo-theta"&gt;
        θ
        &lt;input id="sinus-demo-theta" class="sinus-demo-input" type="number" value="0" step="0.1"&gt;
      &lt;/label&gt;
    &lt;/div&gt;

    &lt;div id="sinus-demo-message" class="sinus-demo-message"&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;script src="https://eli.thegreenplace.net/demos/sinus/sinus-demo.js"&gt;
&lt;/script&gt;&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;The math level of this post is high-school, at best. My main goal
here is to test how to integrate interactive demos into my blog
posts.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;This can be a bit counter-intuitive at first; we scale &lt;img alt="w" class="valign-0" src="https://eli.thegreenplace.net/images/math/aff024fe4ab0fece4091de044c58c9ae4233383a.png" style="height: 8px;" /&gt; by
2, but the period scales by half. Why? The reason is that &lt;img alt="w" class="valign-0" src="https://eli.thegreenplace.net/images/math/aff024fe4ab0fece4091de044c58c9ae4233383a.png" style="height: 8px;" /&gt;
affects the sinusoid’s &lt;em&gt;domain&lt;/em&gt;, while the period is a property of
its &lt;em&gt;range&lt;/em&gt;. Therefore, an inverse relation is reasonable, once we
put more thought into it. In fact, &lt;img alt="w" class="valign-0" src="https://eli.thegreenplace.net/images/math/aff024fe4ab0fece4091de044c58c9ae4233383a.png" style="height: 8px;" /&gt; is often called the
&lt;em&gt;angular frequency&lt;/em&gt; of the sinusoid, and frequency is inverse
proportional to the period.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="Math"></category></entry><entry><title>Thoughts on WebAssembly as a stack machine</title><link href="https://eli.thegreenplace.net/2026/thoughts-on-webassembly-as-a-stack-machine/" rel="alternate"></link><published>2026-04-29T19:28:00-07:00</published><updated>2026-04-30T02:30:02-07:00</updated><author><name>Eli Bendersky</name></author><id>tag:eli.thegreenplace.net,2026-04-29:/2026/thoughts-on-webassembly-as-a-stack-machine/</id><summary type="html">&lt;p&gt;This week the article &lt;a class="reference external" href="https://purplesyringa.moe/blog/wasm-is-not-quite-a-stack-machine/"&gt;Wasm is not quite a stack machine&lt;/a&gt; has been
making the rounds and has caught my eye. The post claims that WASM is not a pure
stack machine because it has locals and is missing some stack manipulation
operations like &lt;tt class="docutils literal"&gt;dup&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;swap&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;While I don't …&lt;/p&gt;</summary><content type="html">&lt;p&gt;This week the article &lt;a class="reference external" href="https://purplesyringa.moe/blog/wasm-is-not-quite-a-stack-machine/"&gt;Wasm is not quite a stack machine&lt;/a&gt; has been
making the rounds and has caught my eye. The post claims that WASM is not a pure
stack machine because it has locals and is missing some stack manipulation
operations like &lt;tt class="docutils literal"&gt;dup&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;swap&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;While I don't necessarily disagree, IMHO it's a bit of a semantic
discussion because - to the best of my knowledge - there is no &lt;em&gt;formal&lt;/em&gt;
definition of what is a stack machine. Wikipedia, for example,
says:&lt;/p&gt;
&lt;blockquote&gt;
[...], a stack machine is a computer processor or a process virtual machine in
which the primary interaction is moving short-lived temporary values to and
from a push-down stack.&lt;/blockquote&gt;
&lt;p&gt;WASM certainly fits this definition; the &lt;em&gt;primary&lt;/em&gt; interaction is through the
stack, though WASM is augmented with an infinite register file (locals).
The more purist stack machines like Forth are only limited to the stack and a
memory (pointers into which are managed on the stack); WASM has these too, plus
the registers.&lt;/p&gt;
&lt;p&gt;Speaking of Forth, the mention of &lt;tt class="docutils literal"&gt;dup&lt;/tt&gt; reminded me of my own impressions
of programming in that language, documented in my post about
&lt;a class="reference external" href="https://eli.thegreenplace.net/2025/implementing-forth-in-go-and-c/"&gt;implementing Forth in Go and C&lt;/a&gt;. There,
I highlighted the following essential library function for Forth; it adds an
addend to a value stored in memory.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;+!&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="c1"&gt;( addend addr -- )&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;tuck&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;( addr addend addr )&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;@&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;( addr addend value-at-addr )&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;+&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="c1"&gt;( addr updated-value )&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;swap&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;( updated-value addr )&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;;&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And lamented how difficult it is to understand such code without the
detailed stack view in comments alongside it.&lt;/p&gt;
&lt;p&gt;I find it much simpler to reason about this WASM code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;add_to_byte&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;param&lt;/span&gt; &lt;span class="nv"&gt;$addr&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;param&lt;/span&gt; &lt;span class="nv"&gt;$delta&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;i32.store8&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="nv"&gt;$addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;i32.add&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;i32.load8_u&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="nv"&gt;$addr&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="nv"&gt;$delta&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You may say this is cheating because folded WASM instructions help readability
and they're just syntactic sugar; OK, here's the linear code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="nv"&gt;$addr&lt;/span&gt;
&lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="nv"&gt;$addr&lt;/span&gt;
&lt;span class="nb"&gt;i32.load8_u&lt;/span&gt;
&lt;span class="nb"&gt;local.get&lt;/span&gt; &lt;span class="nv"&gt;$delta&lt;/span&gt;
&lt;span class="nb"&gt;i32.add&lt;/span&gt;
&lt;span class="nb"&gt;i32.store8&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It's still very readable, because - while the stack is used for all the
calculations and actual commands - some of the data lives in named &amp;quot;registers&amp;quot;
instead of on the stack. So we don't need all those tuck-swap contortions to get
things into the right order.&lt;/p&gt;
&lt;p&gt;One might worry about the duplicated &lt;tt class="docutils literal"&gt;local.get $addr&lt;/tt&gt;; wouldn't a real &lt;tt class="docutils literal"&gt;dup&lt;/tt&gt;
be better? Well, not in terms of readability, as we've already discussed. How
about performance? Since the stack VM is just an abstraction and the underlying
CPUs executing this code are register machines anyway, the answer is no - it
doesn't matter at all.&lt;/p&gt;
&lt;p&gt;Modern compiler engineers were forged in the fires of C and its descendants;
arbitrary control flow, arbitrary register and memory access, anything goes.
Compilers are quite sophisticated. Let's see how &lt;tt class="docutils literal"&gt;wasmtime&lt;/tt&gt; compiles our
&lt;tt class="docutils literal"&gt;add_to_byte&lt;/tt&gt; to native code (using &lt;tt class="docutils literal"&gt;wasmtime explore&lt;/tt&gt; with its
default &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;opt-level=2&lt;/span&gt;&lt;/tt&gt;); comments are added by me:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;// Prologue&lt;/span&gt;
&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rbp&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rbp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rsp&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="c1"&gt;// wasmtime&amp;#39;s VM context pointer lives in rdi; 0x38 is likely its offset&lt;/span&gt;
&lt;span class="c1"&gt;// to the default linear memory. Therefore, r10 will hold the base address&lt;/span&gt;
&lt;span class="c1"&gt;// of the linear memory buffer&lt;/span&gt;
&lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;qword&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rdi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x38&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="c1"&gt;// The first parameter ($addr) is in edx; since WASM values are i32, it&amp;#39;s&lt;/span&gt;
&lt;span class="c1"&gt;// zero-extended into the 64-bit r11 by copying into r11d&lt;/span&gt;
&lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r11d&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;edx&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="c1"&gt;// r10+r11 is memory[$addr]; this loads the current value into rsi&lt;/span&gt;
&lt;span class="c1"&gt;// (zero-extending from 8 bits)&lt;/span&gt;
&lt;span class="n"&gt;movzx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rsi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;byte&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r11&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="c1"&gt;// ecx is the first parameter ($delta); this adds the addend to the&lt;/span&gt;
&lt;span class="c1"&gt;// current value&lt;/span&gt;
&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;esi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ecx&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="c1"&gt;// Store cur_value+addend back into memory[$addr]&lt;/span&gt;
&lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;byte&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;r10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;r11&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sil&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;

&lt;span class="c1"&gt;// Epilogue&lt;/span&gt;
&lt;span class="n"&gt;mov&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rsp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rbp&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="n"&gt;pop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rbp&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="w"&gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is pretty much the code we'd expect to be emitted for the C statement
&lt;tt class="docutils literal"&gt;mem[addr] += addend&lt;/tt&gt;, or if we were writing x86-64 assembly by hand. The
compiler had no difficulty figuring out that two consecutive loads from
the same WASM local produce the same value and do not - in fact - have to be
duplicated. The WASM model makes it rather easy, because you can't alias locals;
as long as there are no intervening writes into the same local, multiple reads
are known to produce the same value (redundant load elimination).&lt;/p&gt;
</content><category term="misc"></category><category term="WebAssembly"></category><category term="Compilation"></category></entry><entry><title>Debugging WASM in Chrome DevTools</title><link href="https://eli.thegreenplace.net/2026/debugging-wasm-in-chrome-devtools/" rel="alternate"></link><published>2026-04-22T19:23:00-07:00</published><updated>2026-04-23T02:24:51-07:00</updated><author><name>Eli Bendersky</name></author><id>tag:eli.thegreenplace.net,2026-04-22:/2026/debugging-wasm-in-chrome-devtools/</id><summary type="html">&lt;p&gt;When I was working on the &lt;a class="reference external" href="https://eli.thegreenplace.net/2026/compiling-scheme-to-webassembly/"&gt;WASM backend for my Scheme compiler&lt;/a&gt;,
I ran into several tricky situations with debugging generated WASM code. It
turned out that Chrome has a very capable WASM debugger in its DevTools, so in
this brief post I want to share how it can be …&lt;/p&gt;</summary><content type="html">&lt;p&gt;When I was working on the &lt;a class="reference external" href="https://eli.thegreenplace.net/2026/compiling-scheme-to-webassembly/"&gt;WASM backend for my Scheme compiler&lt;/a&gt;,
I ran into several tricky situations with debugging generated WASM code. It
turned out that Chrome has a very capable WASM debugger in its DevTools, so in
this brief post I want to share how it can be used.&lt;/p&gt;
&lt;div class="section" id="the-setup-and-harness"&gt;
&lt;h2&gt;The setup and harness&lt;/h2&gt;
&lt;p&gt;I'll be using an example from my &lt;a class="reference external" href="https://github.com/eliben/wasm-wat-samples"&gt;wasm-wat-samples project&lt;/a&gt; for this post. In fact,
everything is already in place in the &lt;a class="reference external" href="https://github.com/eliben/wasm-wat-samples/tree/main/gc-print-scheme-pairs"&gt;gc-print-scheme-pairs&lt;/a&gt;
sample. This sample shows how to construct Scheme-like s-exprs in WASM using gc
references and print them out recursively. The sample supports nested pairs
of integers, booleans and symbols.&lt;/p&gt;
&lt;p&gt;To see this in action, we have to first compile the WAT file to WASM, for
example using &lt;a class="reference external" href="https://github.com/eliben/watgo"&gt;watgo&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ cd gc-print-scheme-pairs
$ watgo parse gc-print-scheme-pairs.wat -o gc-print-scheme-pairs.wasm
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;browser-loader.html&lt;/span&gt;&lt;/tt&gt; file in that directory already expects to load
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gc-print-scheme-pairs.wasm&lt;/span&gt;&lt;/tt&gt;. But we can't just open it
directly from the file-system; since it loads WASM, this file needs to be
served with a local HTTP server. I personally use &lt;a class="reference external" href="https://github.com/eliben/static-server/"&gt;static-server&lt;/a&gt;
for this, but you can use anything else - like Python's built-in &lt;tt class="docutils literal"&gt;http.server&lt;/tt&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;$ static-server
2026/04/10 08:55:20.244096 Serving directory &amp;quot;.&amp;quot; on http://127.0.0.1:8080
...
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now it can be opened in the browser by following the printed link and selecting
the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;browser-loader.html&lt;/span&gt;&lt;/tt&gt; file.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-debugging-process"&gt;
&lt;h2&gt;The debugging process&lt;/h2&gt;
&lt;p&gt;Open the Chrome DevTools, and in &lt;em&gt;Sources&lt;/em&gt;, open the &lt;em&gt;Page&lt;/em&gt; view on the left.
It should have one entry under &lt;em&gt;wasm&lt;/em&gt;, which will show the decompiled WAT
code for our module. Note: this code is disassembled from the binary WASM, so
it will lose some WAT syntactic sugar (like folded instructions):&lt;/p&gt;
&lt;img alt="Screenshot showing where WASM source is in DevTools" class="align-center" src="https://eli.thegreenplace.net/images/2026/wasm-debug-screenshot1.png" /&gt;
&lt;p&gt;You can set a breakpoint by clicking on the address column to the left of the
code, and then refresh the page. The DevTools debugger will run the program
again and stop at the breakpoint:&lt;/p&gt;
&lt;img alt="Screenshot showing debugger stopping on the breakpoint line" class="align-center" src="https://eli.thegreenplace.net/images/2026/wasm-debug-screenshot2.png" /&gt;
&lt;p&gt;Here you can step over, into, see local values and call stack, etc - a real
debugger!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="debugging-unexpected-exceptions"&gt;
&lt;h2&gt;Debugging unexpected exceptions&lt;/h2&gt;
&lt;p&gt;The most important use case for me while developing the compiler was debugging
unexpected exceptions (coming from instructions like &lt;tt class="docutils literal"&gt;ref.cast&lt;/tt&gt;). Notice
the checkboxes saying &amp;quot;Pause on ... exceptions&amp;quot; on the right-hand side of the
previous screenshot. With these selected, the DevTools debugger will
automatically stop on an exception and show where it is coming from. Let's
modify the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;gc-print-scheme-pairs.wat&lt;/span&gt;&lt;/tt&gt; sample to see this in action. The
&lt;tt class="docutils literal"&gt;$emit_value&lt;/tt&gt; function performs a set of &lt;tt class="docutils literal"&gt;ref.test&lt;/tt&gt; checks to see which kind
of reference it's dealing with before casting; let's add this line at the
very start:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;(call $emit_bool (ref.cast (ref $Bool) (local.get $v)))
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It's clearly wrong to assume that &lt;tt class="docutils literal"&gt;$v&lt;/tt&gt; is a bool reference without first
testing it; this is just for demonstration purposes.&lt;/p&gt;
&lt;p&gt;Without setting any breakpoints, recompiling this code with &lt;tt class="docutils literal"&gt;watgo&lt;/tt&gt; and
reloading the page, we get:&lt;/p&gt;
&lt;img alt="Screenshot showing debugger stopping on an exception" class="align-center" src="https://eli.thegreenplace.net/images/2026/wasm-debug-screenshot3.png" /&gt;
&lt;p&gt;The debugger stopped at the instruction causing the exception; moreover, in the
&lt;em&gt;Scope&lt;/em&gt; pane on the right we can see that the actual type of &lt;tt class="docutils literal"&gt;$v&lt;/tt&gt; is
&lt;tt class="docutils literal"&gt;(ref $Pair)&lt;/tt&gt;, so it's immediately clear what's going on.&lt;/p&gt;
&lt;p&gt;I've found this capability extremely valuable when writing (or emitting from
a compiler) non-trivial chunks of WASM code using gc types and instructions.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="debugger-vs-printfs-in-wasm"&gt;
&lt;h2&gt;Debugger vs. printfs in wasm&lt;/h2&gt;
&lt;p&gt;&amp;quot;Should I use a debugger or just printfs&amp;quot; is a common topic of debate among
programmers. While I'm usually in the &amp;quot;printf debugging&amp;quot;
camp, I'm not dogmatic, and will certainly reach for a debugger when
the situation calls for it.&lt;/p&gt;
&lt;p&gt;Specifically, when investigating reference exceptions in WASM, two strong
factors tilt the decision towards using a debugger:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;In general, WASM's printf capabilities aren't great. We can import print-like
functions from the host (and - in fact - our sample does just that), but
they're not very flexible and dealing with strings in WASM is painful in
general. This is compounded even more when working with gc types, because
these aren't even visible to the host (they're opaque references). If we want
to do printf debugging of gc values, we have to build &lt;em&gt;a lot&lt;/em&gt; of scaffolding
first.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Exception debugging - in general - is much easier with a supportive debugger
in hand. Our &lt;tt class="docutils literal"&gt;ref.cast&lt;/tt&gt; exception from the example above could have
happened &lt;em&gt;anywhere&lt;/em&gt; in the code. Imagine having to debug a very large
WASM program (emitted by a compiler) to find the source of a failed
&lt;tt class="docutils literal"&gt;ref.cast&lt;/tt&gt;; the debugger takes you right to the spot!&lt;/p&gt;
&lt;p&gt;In fact, even for C programming, I've always found &lt;tt class="docutils literal"&gt;gdb&lt;/tt&gt; most useful for
pinpointing the source of segmentation faults and similar crashes.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="WebAssembly"></category><category term="Debuggers"></category><category term="JavaScript"></category></entry></feed>