<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"
    xmlns:dc="http://purl.org/dc/elements/1.1/">
    <channel>
        <title>Craig Stuntz's blog</title>
        <link>https://www.craigstuntz.com</link>
        <description><![CDATA[Craig Stuntz's blog]]></description>
        <atom:link href="https://www.craigstuntz.com/feed.xml" rel="self"
                   type="application/rss+xml" />
        <lastBuildDate>Mon, 11 May 2026 00:00:00 UT</lastBuildDate>
        <item>
    <title>You've Been Constraint-Solving All Along</title>
    <link>https://www.craigstuntz.com/posts/2026-05-11-constraint-programming.html</link>
    <description><![CDATA[<div class="info">
    Posted on May 11, 2026
    
</div>

<p>There’s a pattern which shows up everywhere in programming, often disguised as
something else. Once you see it, you can’t unsee it. I don’t <em>think</em> it has a
single name. But I can give many examples! The pattern is, in an abstract sense:</p>
<ol type="1">
<li><strong>Generation</strong>: Take an input dataset and apply a function to project it into a different shape</li>
<li><strong>Restriction</strong>: Write another function which restricts the results of that function to only
those which are “interesting”</li>
<li><strong>Reduction</strong> (optional): Write yet another function which reduces this to a single value,
say, by summing the results or picking the minimum, or just taking the first
result that comes in</li>
</ol>
<p>The Prolog community called the naïve form of this “generate-and-test” since at least
the 1980s:</p>
<blockquote>
<p>“Generate-and-test is a common technique in algorithm design and programming.
Here is how generate-and-test works for problem solving. One process or
routine generates candidate solutions to the problem, and another process or
routine tests the candidates, trying to find one or all candidates that
actually solve the problem.”<br/>
– <em>The Art of Prolog</em> by Leon Sterling and Ehud Shapiro (1986)</p>
</blockquote>
<p>Formally, this is called the
<a href="https://en.wikipedia.org/wiki/Constraint_satisfaction_problem"><strong>constraint satisfaction problem</strong></a>.</p>
<h2 id="the-abstraction">The Abstraction</h2>
<p>In case this isn’t absolutely clear, let’s look at some JavaScript which uses the
brute force method to solve <a href="https://projecteuler.net/problem=9">problem 9 of Project Euler</a>:</p>
<blockquote>
<p>A Pythagorean triplet is a set of three natural numbers, <span class="math inline"><em>a</em> &lt; <em>b</em> &lt; <em>c</em></span>, for which
<span class="math display">$$ \begin{align} a^2 + b^2 = c^2 \end{align} $$</span>
For example, <span class="math inline">3<sup>2</sup> + 4<sup>2</sup> = 9 + 16 = 25 = 5<sup>2</sup></span>.<br/>
There exists exactly one Pythagorean triplet for which <span class="math inline"><em>a</em> + <em>b</em> + <em>c</em> = 1000</span>.<br/>
Find the product <span class="math inline"><em>a</em><em>b</em><em>c</em></span>.</p>
</blockquote>
<p>Ok, we can do this in a one-liner:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> result <span class="op">=</span> </span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Generate numbers [a, b, c] where a + b + c = 1000 and a &lt; b &lt; c</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>  <span class="bu">Array</span>(<span class="dv">1000</span>)<span class="op">.</span><span class="fu">keys</span>()<span class="op">.</span><span class="fu">flatMap</span>(a <span class="kw">=&gt;</span> <span class="bu">Array</span>(<span class="dv">1000</span>)<span class="op">.</span><span class="fu">keys</span>()<span class="op">.</span><span class="fu">filter</span>(b <span class="kw">=&gt;</span> a <span class="op">&lt;</span> b <span class="op">&amp;&amp;</span> b <span class="op">&lt;</span> <span class="dv">1000</span><span class="op">-</span>a<span class="op">-</span>b)<span class="op">.</span><span class="fu">map</span>(b <span class="kw">=&gt;</span> [a<span class="op">,</span> b<span class="op">,</span> <span class="dv">1000</span><span class="op">-</span>a<span class="op">-</span>b]))</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    <span class="co">// restrict to Pythagorean triples</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>    <span class="op">.</span><span class="fu">filter</span>(([a<span class="op">,</span>b<span class="op">,</span>c]) <span class="kw">=&gt;</span> a<span class="op">*</span>a <span class="op">+</span> b<span class="op">*</span>b <span class="op">===</span> c<span class="op">*</span>c) </span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Reduce to the product</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">.</span><span class="fu">map</span>(([a<span class="op">,</span>b<span class="op">,</span>c]) <span class="kw">=&gt;</span> a<span class="op">*</span>b<span class="op">*</span>c)<span class="op">.</span><span class="fu">take</span>(<span class="dv">1</span>)<span class="op">.</span><span class="fu">toArray</span>()[<span class="dv">0</span>]<span class="op">;</span></span></code></pre></div>
<p>This looks incredibly simple! It’s not a “pattern,” it’s one line of code!</p>
<p>The abstraction really is that simple, and the implementations try to stay close to
that abstraction as much as possible. However, there can be a surprising amount
of complexity in how the code is executed.</p>
<p>Perhaps you’ve been programming for a long time and consider all of this to
be very obvious? Fine, you may not get a lot from this post! However, I do
remember when I was new to software development and how I might solve a Project
Euler problem was not obvious to me! One thing that I specifically did not
understand at that point was that brute force might be the right solution to a
problem <em>at least in an abstract sense.</em> Having written a solution in terms of
brute force we can then optimize our solution if the performance is inadequate.</p>
<p>I will further say that when I hear some of the dialog around agent-based
programming, specifically the “it might hallucinate!” or the “look how fast I can
blast out garbage!” arguments I hear from both “sides,” that I am reminded of
this pattern. If you don’t restrict your solutions, with comprehensive tests or
proofs, then yes, you’re going to get errors, whether you generate your possible
solutions with AI or by hand.</p>
<h2 id="worked-out-examples">Worked Out Examples</h2>
<p>The same pattern keeps coming up, over and over again. Also, the implementations
of this pattern vary quite a lot, so there is depth behind the seeming
simplicity. You can implement this pattern in various languages, and they will
be executed differently!</p>
<h3 id="mapreduce">MapReduce</h3>
<p><a href="https://en.wikipedia.org/wiki/MapReduce">Classic MapReduce</a> would seem to be a
direct mapping of the pattern above, but I will note that it’s more of a
distributed <em>execution</em> paradigm. Running a Project Euler problem on a cluster
would be a bit silly and probably missing the point of Project Euler. But you can
certainly see how the “map” (generate + restrict) and “reduce” fit in here.</p>
<h3 id="smt-libz3">SMT-LIB/Z3</h3>
<p>Are you ready to get weird? Here is the same Problem 9 from Project
Euler implemented in SMT-LIB, a pure specification language:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode lisp"><code class="sourceCode commonlisp"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co">; SMT-LIB is pretty basic and doesn&#39;t have exponentiation built in</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="co">; So we&#39;ll define this for legibility</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>(define-fun square ((num Int)) Int (<span class="op">*</span> num num))</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="co">; The &quot;generation&quot; is &quot;all possible assignments to a, b, c&quot;</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>(declare-const a Int)</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>(declare-const b Int)</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>(declare-const c Int)</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="co">; We restrict it to &quot;those which satisfy the specification&quot; by just writing it out!</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="co">; We are looking for a set of three natural numbers, a &lt; b &lt; c</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>(<span class="kw">assert</span> (<span class="kw">and</span> (<span class="op">&gt;=</span> a <span class="dv">0</span>) (<span class="op">&lt;</span> a b) (<span class="op">&lt;</span> b c)))</span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a><span class="co">; for which a^2 + b^2 = c^2</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a>(<span class="kw">assert</span> (<span class="op">=</span> (<span class="op">+</span> (square a) (square b)) (square c)))</span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a><span class="co">; for which a + b + c = 1000</span></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a>(<span class="kw">assert</span> (<span class="op">=</span> (<span class="op">+</span> a b c) <span class="dv">1000</span>))</span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a><span class="co">; And we want to return the product, since Project Euler needs a single number</span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a>(declare-const product Int)</span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a>(<span class="kw">assert</span> (<span class="op">=</span> product (<span class="op">*</span> a b c)))</span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a><span class="co">; This checks if there&#39;s any solution at all</span></span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a>(check-sat)</span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a><span class="co">; This returns the solution</span></span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true" tabindex="-1"></a>(get-model)</span></code></pre></div>
<p>If we hand this specification to a solver such as <a href="/tags/z3.html">Z3</a>, it will
use decision procedures to solve the constraints directly rather than enumerate
with brute force. <a href="https://microsoft.github.io/z3guide/playground/Freeform%20Editing/">Try it!</a></p>
<p>It is less the case here that we are “generating” triples of integers and
restricting them to those for which <span class="math inline"><em>a</em><sup>2</sup> + <em>b</em><sup>2</sup> = <em>c</em><sup>2</sup></span> and more the case that
we are generating possible assignments to <code>a</code>, <code>b</code>, and <code>c</code> and restricting them
to those which <em>correctly satisfy the specification we have given.</em></p>
<h3 id="prolog">Prolog</h3>
<p>Similarly, Prolog lets us directly encode the problem from the specification:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode prolog"><code class="sourceCode prolog"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>answer(<span class="dt">Product</span>) <span class="kw">:-</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    between(<span class="dv">0</span><span class="kw">,</span> <span class="dv">998</span><span class="kw">,</span> <span class="dt">A</span>)<span class="kw">,</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>    <span class="co">% a &lt; b</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>    between(<span class="dt">A</span><span class="kw">,</span> <span class="dv">998</span><span class="kw">,</span> <span class="dt">B</span>)<span class="kw">,</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>    <span class="co">% a + b + c = 1000</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>    <span class="dt">C</span> <span class="dt">is</span> <span class="dv">1000</span> <span class="dt">-</span> <span class="dt">A</span> <span class="dt">-</span> <span class="dt">B</span><span class="kw">,</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>    <span class="dt">B</span> <span class="dt">&lt;</span> <span class="dt">C</span><span class="kw">,</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>    <span class="co">% a^2 + b^2 = c^2</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>    <span class="dt">A</span><span class="fu">*</span><span class="dt">A</span> <span class="fu">+</span> <span class="dt">B</span><span class="fu">*</span><span class="dt">B</span> <span class="dt">=:=</span> <span class="dt">C*C</span><span class="kw">,</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Product</span> <span class="dt">is</span> <span class="dt">A</span> <span class="dt">*</span> <span class="dt">B</span> <span class="dt">*</span> <span class="dt">C</span><span class="kw">.</span></span></code></pre></div>
<p>One way to think about what Prolog is doing here is that we are not so much
writing a generator and restriction function as we are <em>binding</em> the input
arguments <code>A</code>, <code>B</code>, and <code>C</code> to the output <code>Product</code>.</p>
<h2 id="still-more-examples">Still More Examples</h2>
<p>I will spare you the implementation of Project Euler problem 9 in SQL, but this
pattern is applicable to many problems in programming!</p>
<p>For example, when <a href="https://en.wikipedia.org/wiki/Fuzzing">fuzzing</a>, we take a
bunch of random inputs, restrict them to those which crash or trigger
<a href="https://github.com/google/sanitizers/wiki/addresssanitizer">AddressSanitizer</a>,
and then reduce the output by running a <a href="https://arxiv.org/abs/1905.13055">corpus minimization</a>.</p>
<p>When doing agent driven development, we can generate many programs very quickly,
but they might be incorrect! If we have a formal proof of correctness then we
can reduce the set of candidate programs to those which satisfy the
specification. Agents turn out to be quite good at finding such programs.
But without a proof, we might end up with something sloppy!</p>
<p>In classic TDD, we start with a system under test which we are modifying
(in effect, generating lots of candidate programs). Our restriction is the test
suite, and we reduce this via a pass/fail verdict – do the tests pass?</p>
<h2 id="measuring-generation-and-restriction-efficiency">Measuring Generation and Restriction Efficiency</h2>
<p>The last example, TDD, might feel a bit unsatisfactory. One can imagine trying
to solve Project Euler problem 9 via TDD:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode java"><code class="sourceCode java"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Arrange -- choose some random inputs as a first trial</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">var</span> a <span class="op">=</span> <span class="dv">1</span><span class="op">;</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">var</span> b <span class="op">=</span> <span class="dv">2</span><span class="op">;</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">var</span> c <span class="op">=</span> <span class="dv">3</span><span class="op">;</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Act</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>  <span class="dt">var</span> actual <span class="op">=</span> Euler<span class="op">.</span><span class="fu">sumOfSquares</span><span class="op">(</span>a<span class="op">,</span> b<span class="op">);</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Assert</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>  Assert<span class="op">.</span><span class="fu">assertTrue</span><span class="op">(</span>a <span class="op">&lt;</span> b<span class="op">);</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>  Assert<span class="op">.</span><span class="fu">assertTrue</span><span class="op">(</span>b <span class="op">&lt;</span> c<span class="op">);</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>  Assert<span class="op">.</span><span class="fu">assertEquals</span><span class="op">(</span><span class="dv">1000</span><span class="op">,</span> a <span class="op">+</span> b <span class="op">+</span> c<span class="op">);</span> <span class="co">// Fail!</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>  Assert<span class="op">.</span><span class="fu">assertEquals</span><span class="op">(</span>c <span class="op">*</span> c<span class="op">,</span> actual<span class="op">);</span>   <span class="co">// Fail!</span></span></code></pre></div>
<p>I kid, I kid. Java can do brute force, just like JavaScript, even if TDD purists
might sincerely suggest the above as a good starting point. But nonetheless,
this example does indicate a difficulty with TDD: Finding meaningful inputs for
your problem space.</p>
<blockquote>
<p>“…program testing can be used very effectively to show the presence of bugs
but never to show their absence.”<br/>
– Edsger W. Dijkstra, <a href="https://www.cs.utexas.edu/~EWD/transcriptions/EWD03xx/EWD303.html">On the reliability of programs</a></p>
</blockquote>
<p>This might be a helpful way to judge implementations of the constraint
satisfaction problem: How much data does the generation produce, and how
effective is the restriction step? Can programs written using this pattern go
wrong?</p>
<p>Let’s consider the problem of finding bugs in source code you’ve produced. We
generate candidate bugs and then apply some test to figure out which candidates
are actually defects. (For example, a
<a href="https://en.wikipedia.org/wiki/Static_program_analysis">static analyzer</a> might
look for string concatenation in SQL statements and then attempt to restrict
reporting to those instances where the data being concatenated is user-controlled.)
Generation can be too sparse to find bugs or so dense it overwhelms you.
Restriction can let real bugs through or flag false positives. Both axes can fail
in two directions.</p>
<table style="border: none;">
<tr>
<td style="border: none;">
</td>
<td style="border: none;">
</td>
<th colspan="3">
Restriction
</th>
</tr>
<tr>
<td style="border: none;">
</td>
<td style="border: none;">
</td>
<th>
Not restrictive enough
</th>
<th>
“Just right”
</th>
<th>
Too restrictive
</th>
</tr>
<tr>
<th rowspan="4" style="overflow-wrap: break-word;">
Generator<br/><span style="font-weight: normal;">(number of cases considered)</span>
</th>
<th>
Sampled
</th>
<td>
TDD
</td>
<td>
Property-based testing
</td>
<td>
Overfitted unit tests
</td>
</tr>
<tr>
<th>
“Just right”
</th>
<td>
Smoke testing
</td>
<td>
Correctness proofs
</td>
<td>
Rust’s borrow checker<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>
</td>
</tr>
<tr>
<th>
“A lot”
</th>
<td>
<code>printf</code> debugging
</td>
<td>
Profile-guided fuzzing
</td>
<td>
Static analysis
</td>
</tr>
<tr>
<th>
Too much
</th>
<td>
Manually skimming a log file
</td>
<td>
Symbolic execution
</td>
<td>
Mutation testing
</td>
</tr>
</table>
<p>Lest you consider me too judgy for the table above, I don’t mean to suggest that
you should not, for example, use static analysis due to false positives!
Static analysis is a <em>fantastic</em> way to avoid bugs, even if you must occasionally
suppress a false positive. Still, the <em>ideal</em> static analyzer would have no false positives.
Indeed, <em>all</em> of the techniques listed above have their place, but I do think
that the table is honest about their shortcomings.</p>
<h2 id="embracing-constraint-solving">Embracing Constraint Solving</h2>
<p>These examples show the constraint satisfaction problem hiding in plain sight.
Every tool above is making the same two decisions any constraint-satisfaction
approach has to make: how to generate candidates, and how to test them. SMT
solvers generate via decision procedures and test via assertions. Fuzzers
generate via mutation and test via sanitizers. Type checkers generate via
inference and test via unification. Even TDD fits the pattern, with the developer
playing the role of the generator.</p>
<p>Recognizing this pattern lets you compare tools on the same axes — execution
model, generation density, and restriction tightness. Pick the one whose
performance and failure mode you can live with for the problem in front of you.</p>
<p>The most important thing I want you to take away from this post is to look for
constraint solving solutions to programming problems in front of you. When
you see one, don’t be put off by questions of efficiency or false positives.
There are solutions to those! But by recognizing <em>the abstraction</em> you can focus
on the most important part, which is <em>correct</em> generation and restriction.</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>Especially the
<a href="https://oneuptime.com/blog/post/2026-01-25-non-lexical-lifetimes-rust/view">pre-NLL borrow checker</a>,
although safe Rust will always reject valid (but not provably memory-safe)
programs. To be clear, I think this is a good thing!<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>

<div class="info">
    
    Tags: <a title="All pages tagged &#39;computer science&#39;." href="/tags/computer%20science.html" rel="tag">computer science</a>, <a title="All pages tagged &#39;constraint satisfaction problem&#39;." href="/tags/constraint%20satisfaction%20problem.html" rel="tag">constraint satisfaction problem</a>, <a title="All pages tagged &#39;javascript&#39;." href="/tags/javascript.html" rel="tag">javascript</a>, <a title="All pages tagged &#39;patterns&#39;." href="/tags/patterns.html" rel="tag">patterns</a>, <a title="All pages tagged &#39;prolog&#39;." href="/tags/prolog.html" rel="tag">prolog</a>, <a title="All pages tagged &#39;tdd&#39;." href="/tags/tdd.html" rel="tag">tdd</a>, <a title="All pages tagged &#39;theorem provers&#39;." href="/tags/theorem%20provers.html" rel="tag">theorem provers</a>, <a title="All pages tagged &#39;z3&#39;." href="/tags/z3.html" rel="tag">z3</a>
    
</div>
]]></description>
    <pubDate>Mon, 11 May 2026 00:00:00 UT</pubDate>
    <guid>https://www.craigstuntz.com/posts/2026-05-11-constraint-programming.html</guid>
    <dc:creator>Craig Stuntz</dc:creator>
</item>
<item>
    <title>Building a Synthesizer, Chapter 14: Building the Sequencer</title>
    <link>https://www.craigstuntz.com/posts/2026-02-05-building-a-synthesizer-14.html</link>
    <description><![CDATA[<div class="info">
    Posted on February  5, 2026
    
</div>

<div class="toc">
<ul>
<li><a href="2023-02-20-building-a-synthesizer-0.html">Introduction: The World of DIY Synthesizers</a></li>
<li><a href="2023-02-21-building-a-synthesizer-1.html">1: The mki x es.EDU DIY System</a></li>
<li><a href="2023-02-22-building-a-synthesizer-2.html">2: Building the Power Supply</a></li>
<li><a href="2023-03-02-building-a-synthesizer-3.html">3: Breadboarding the VCO</a></li>
<li><a href="2023-04-03-building-a-synthesizer-4.html">4: A Gentle Introduction to Op Amps</a></li>
<li><a href="2023-05-22-building-a-synthesizer-5.html">5: Building the VCO</a></li>
<li><a href="2023-08-11-building-a-synthesizer-6.html">6: The Logic Circuits Model of Computation</a></li>
<li><a href="2023-09-21-building-a-synthesizer-7.html">7: Building the Mixer</a></li>
<li><a href="2024-01-31-building-a-synthesizer-8.html">8: Building the Envelope Generator</a></li>
<li><a href="2024-02-21-building-a-synthesizer-9.html">9: A Field Guide to Oscillators</a></li>
<li><a href="2024-06-24-building-a-synthesizer-10.html">10: Building the VCA</a></li>
<li><a href="2025-04-07-building-a-synthesizer-11.html">11: Debugging Circuits and Software Debugging</a></li>
<li><a href="2025-09-09-building-a-synthesizer-12.html">12: Breadboarding the VCF</a></li>
<li><a href="2025-12-27-building-a-synthesizer-13.html">13: Building the VCF</a></li>
<li>14: Building the Sequencer</li>
<li><a href="2023-02-23-building-a-synthesizer-glossary.html">Glossary and Electrical Connections</a></li>
</ul>
</div>
<p>The mxi x es.EDU sequencer can play loops of 3, 4, or 5 notes. It can output a
new control voltage, which is commonly routed to
<a href="2023-03-02-building-a-synthesizer-3.html">oscillator</a> pitch, and a gate, which is
commonly routed to an
<a href="2024-01-31-building-a-synthesizer-8.html">envelope generator</a>, on each step of the sequence. Why 5?
In the instructions, Moritz Klein says, “I simply made the decision to build a
sequencer with five steps exactly,” although I suspect the fact that this is
already a fairly complicated build and adding more steps would have only
increased the complexity might have figured into it.</p>
<p>Also, why limit your sequencer to only 4 steps? This one goes to <s>11</s> 5!</p>
<p>Commercial hardware sequencers often feature maximum sequence lengths of 16, 32,
64, or even higher number of steps, but that would make the module very large,
both in terms of the number of components you would need to assemble and in
terms of its physical size. As it is, this is the first module I have built from
this series which requires two full-sized breadboards to prototype.</p>
<h2 id="breadboarding">Breadboarding</h2>
<p>The headings in this section correspond to the headings in
<a href="https://www.ericasynths.lv/media/SEQ_MANUAL_v3.pdf">the instructions for this kit</a>,
in case you would like to follow along.</p>
<p>Breadboarding the sequencer gave me a somewhat surprising amount of trouble!
Part of this is due to the fact that I didn’t have two full-size breadboards
available. Instead I used a full sized breadboard and two half-sized
breadboards. This meant that I had to perform some mental arithmetic when
connecting leads to the op amp chip, which is a great opportunity to make
mistakes!</p>
<h3 id="counting-with-chips-and-the-clock-generator">Counting with Chips and the Clock Generator</h3>
<p>The first step is to build a five step LED counter and clock generator. When I
turned on this circuit, I saw no LEDs lighting.</p>
<p>Well, this was a good opportunity to practice
<a href="2025-04-07-building-a-synthesizer-11.html">my debugging workflow</a>. The first step
is to look for obvious problems such as power. And this very well could have
been the issue as this module requires two breadboards, and the power
distribution of +12V, -12V, and GND across two breadboards is somewhat complex!
But testing with my multimeter showed I had wired this correctly. I also checked
that I had connected the correct IC pins to the power rails, which is a little
bit tricky here as there are two ICs with different power pins, but I had done
that correctly as well.</p>
<p>The second step is to have a model of the circuit. Here, my model is quite
simple: A square wave clock generator makes the sequencer step with every pulse,
causing the LEDs to light up one at a time. So given my observation that none of
the LEDs were lighting up, the issue could have been that I had miswired all of
them, that the sequencer IC was dead, or that the clock generator was not
pulsing.</p>
<p>I suspected the clock generator, and testing with my oscilloscope confirmed that
it was not generating a square wave. Looking closer at that IC it turned out
that I had made an “off by one” error on some of the wiring on the breadboard –
I had used pin 51 instead of pin 50, for example. Fixing this made the LEDs
light up.</p>
<h3 id="resetting-the-loop">Resetting the Loop</h3>
<p>The next step was to limit the sequence to 5 steps (instead of 8 steps,
supported by the chip, but not required for this module). The instructions say:</p>
<blockquote>
<p>So by connecting step six to the reset pin, we
jump back to the first step as soon as we try to move
past step five. Testing this on the breadboard is as easy
as connecting step six and the reset pin with a jumper.</p>
</blockquote>
<p>In fact, an additional step is necessary.
I added a jumper as described and I found that it had no effect! I turned
off the circuit, and tested continuity between the IC pins; there was no problem
there. I thought that perhaps the breadboard wasn’t making a good connection,
but this was fine.</p>
<p>(I will use this symbol <span title="Instructions require clarification" style="color: red;">⚠</span>
when I discuss areas where I think the instructions require clarification.)</p>
<p>Next, I turned on the power and tested the voltage along this wire with my
oscilloscope. I saw no jump when the sequencer got to step 6, although further
testing showed that it did jump on step 7 and 8. Then a light went on for me.
The reason that the voltage didn’t change was that in the previous step we had
wired the reset pin to ground. Jumpering the reset pin to pin 6 meant that pin
6 was now wired to ground, which is where all the electrons were now going!
<span class="warning" title="Instructions require clarification">⚠</span>
So looking more closely at the instructions, the diagram does show the jumper
to ground removed in this step, but it’s not mentioned in the text. Removing the
connection between ground and the reset pin made the circuit work.</p>
<figure>
<a href="/images/synth/SequencerResettingBreadboard.jpeg">
<img src="/images/synth/SequencerResettingBreadboard.jpeg" loading="lazy" height="300px" alt="Three breadboards, one full sized and two half size below it. There are five LEDs along the top. The first one is lit">
</a>
</figure>
<h3 id="the-cv-output">The CV Output</h3>
<p>Next we (temporarily!) disconnect the LEDs and instead wire up five
potentiometers for setting the pitch CV at each step of the sequence.</p>
<p>This was challenging due to breadboard issues. Both the “split” lower breadboard
and the usual breadboard issues (poor connections) presented difficulties, but
I eventually got it working. It transpired that the five potentiometers along the
bottom of the board were not making good electrical contact with the inside of
the breadboard, but figuring out which components were not connecting well was
challenging!</p>
<figure class="horizontalTiles">
<a href="/images/synth/SequencerFiveSteps.png">
<img src="/images/synth/SequencerFiveSteps.png" loading="lazy" height="300px" alt="The oscilloscope, showing the output CV, which has five visible steps, going up">
</a>
</figure>
<figure class="horizontalTiles">
<a href="/images/synth/SequencerCVOutputBreadboard.jpeg">
<img src="/images/synth/SequencerCVOutputBreadboard.jpeg" loading="lazy" height="300px" alt="The breadboard at the CV output stage, showing five new potentiometers and a jack socket with a red wire connected to it">
</a>
</figure>
<div style="clear:both  ;">

</div>
<h3 id="cv-scaling">CV Scaling</h3>
<p>The next step is to scale the range of the sequencer output down from 0-12V to
0-5V. We do this by adding a resistor and a trimpot to reduce the voltage going
into the buffer op amp at the output. I had a couple of problems here. The first
was that the indicated placement of the trimpot was right on the border between
my two half breadboards. <span class="warning" title="Instructions require clarification">⚠</span> The second was that the indicated placement wouldn’t
have worked even if I had a full-sized breadboard, because the instructions call
for a trimpot which takes only a single row of the breadboard (like the trimpots
supplied with other kits in this series) and there are only two rows on the
breadboard free for it in the suggested layout, but the actual trimpot supplied with
this kit is square and needs three rows:</p>
<figure class="horizontalTiles">
<a href="/images/synth/SequencerTrimpot.png">
<img src="/images/synth/SequencerTrimpot.png" loading="lazy" height="150px" alt="A detail from the instructions, showing the trimpot spread across a single row of the breadboard">
</a>
<figcaption>
Instructions, one row
</figcaption>
</figure>
<figure class="horizontalTiles">
<a href="/images/synth/SequencerTrimpotBreadboard.jpg">
<img src="/images/synth/SequencerTrimpotBreadboard.jpg" loading="lazy" height="150px" alt="A photo of the actual trimpot supplied with the kit, which requires 5 rows on the breadboard">
</a>
<figcaption>
Supplied component, three rows
</figcaption>
</figure>
<div style="clear:both  ;">

</div>
<p>So I had to do some gymnastics with my breadboard routing.</p>
<p>Second, I had a momentary heart attack because the bill of materials says that
two 2kΩ trimpots (and one 5kΩ) are supplied, and I only got one.
<span class="warning" title="Instructions require clarification">⚠</span>
But this seems
to be an error, as only one 5kΩ and one 2kΩ trimpots are needed to build or
breadboard the kit.</p>
<p>In the end, though, everything worked, and this step gave the five potentiometers
used to adjust the pitch of the sequenced notes a more useful range.</p>
<h3 id="status-leds">Status LEDs</h3>
<p><span class="warning" title="Instructions require clarification">⚠</span>
Wiring the LEDs again <em>seemed</em> pretty simple, but at first they didn’t work.
The directions, which so carefully specified their orientation when we first
placed them on the breadboard, don’t mention moving them, and more importantly don’t
mention that you have to rotate them 180º, which is less than obvious in the
illustration:</p>
<figure>
<a href="/images/synth/SequencerLEDsFirst.png">
<img src="/images/synth/SequencerLEDsFirst.png" loading="lazy" height="137px" alt="Detail of the instructions, from early on in the kit assembly. The cathode of the LEDs, indicated by a flat surface, is on the right">
</a>
<figcaption>
We started like this…
</figcaption>
</figure>
<figure>
<a href="/images/synth/SequencerLEDsSecond.png">
<img src="/images/synth/SequencerLEDsSecond.png" loading="lazy" height="141px" alt="Detail of the instructions, from the current step. The cathode of the LEDs is on the left">
</a>
<figcaption>
…and here is the current step
</figcaption>
</figure>
<p>However, once I figured out what my mistake was, everything worked:</p>
<figure>
<a href="/images/synth/SequencerStatusLEDsBreadboard.jpeg">
<img src="/images/synth/SequencerStatusLEDsBreadboard.jpeg" loading="lazy" height="300px" alt="Three breadboards, one full sized and two half size below it. There are five LEDs along the top. The first one is lit">
</a>
</figure>
<h3 id="the-gate-output">The Gate Output</h3>
<p>I found this section of the directions a little difficult to understand until I
skipped backwards in the directions to the section entitled “Sequencer Basics.”
My confusion mostly stemmed from the fact that we don’t connect the gate switches
when breadboarding. This is quite understandable given how full the breadboards
are without them, but it made understanding the reason for the gate output
sub-circuit more difficult to intuit.
That section clarified how the switches were supposed to work, which, in turn,
made the explanation of how this sub-circuit worked clearer. Also, looking at
<a href="https://tinyurl.com/yatgsglm">the simulation</a> helped. After that, it was just
a lot of wiring, which did indeed produce a square wave output at the gate jack.
Which feels like a lot of work given that we already have a square wave at the
clock output, but the switches, which we don’t wire when breadboarding the
circuit, will allow us to “disable/suppress” some of the square wave cycles when
we build the actual circuit, resulting in only some of the five notes triggering
the gate output.</p>
<h3 id="the-failsafe-gate-output">The Failsafe Gate Output</h3>
<p>This step was quite easy to breadboard. In order to change the gate output from
one (breadboarded in the step above) which ranges from 0V-6V to one which ranges
from 0V-12V, we replace the op amp with a voltage
divider at the input with a comparitor (which changes the output to either -12V or 12V),
followed by a diode and resisitor (limiting the voltage to just positive values,
so that it becomes either 0V or 12V), followed by a
transistor emitter-follower buffer. Why this configuration (comparitor, diode,
and transistor) and not just replace the op amp buffer before
the gate output with an op amp in amplifier configuration? I don’t know (I guess
maybe you might be concerned that an external source would give you a gate which
is not a square wave, and hence you might want the comparitor in the signal path?), but in
the end the circuit works well enough.</p>
<figure>
<a href="/images/synth/SequencerFailsafeGate.png">
<img src="/images/synth/SequencerFailsafeGate.png" loading="lazy" height="300px" alt="An oscilloscope display, showing a square wave">
</a>
<figcaption>
It goes up and it goes down. It’s a gate!
</figcaption>
</figure>
<h2 id="building-the-sequencer">Building the Sequencer</h2>
<p>In contrast to the other modules I have built from the mki x es.EDU series, the
sequencer has only two horizontally mounted resistors, and the rest of the
resistors are mounted vertically in order to squeeze all of the components onto
the board. Although it doesn’t look much more complicated, perhaps due to the
vertical resistors, there are probably 50% more parts on this board than in the
other modules I have built so far. In general this module required more soldering than other modules
from the series. Despite this, assembly went smoothly and everything worked the
first time I tried powering it up.</p>
<p><span class="warning" title="Instructions require clarification">⚠</span>
The 1nF capacitor (C2) was ceramic, per the supplied parts and the bill of
materials. However, the soldering instructions and the photos showed a film
capacitor instead. Because the soldering instructions have you insert ceramic
capacitors and film capacitors in separate steps, they only show 5 ceramic
capacitors (C3-C8) in the first, ceramic capacitor step. This means that they
don’t call out the fact that one of the supplied ceramic capacitors is a 1nF
capacitor, in contrast to the remaining 5, which are 0.1µF, although all of them
look identical except for the printed values on the side.</p>
<p>When soldering non-flush-mounted components, such as vertical resistors and
transistors, I find it helpful to solder one lead, then turn the board over to correct
the position of the component, then solder the other leads.</p>
<p>Here are some photos of the finished build:</p>
<figure class="horizontalTiles">
<a href="/images/synth/SequencerFrontPanel.jpeg">
<img src="/images/synth/SequencerFrontPanel.jpeg" loading="lazy" height="300px" alt="The front panel of the sequencer. There is a switch for step count, a pot for rate, five switches for gate with five pots for level, unoccupied jacks for clock in, out, and reset, and jacks with cables connected for gate out and CV out. A switch for 2.5V or 5V is hidden by the bottom cable.">
</a>
<figcaption>
Front
</figcaption>
</figure>
<figure class="horizontalTiles">
<a href="/images/synth/SequencerPCBoard.jpeg">
<img src="/images/synth/SequencerPCBoard.jpeg" loading="lazy" height="100px" alt="A side view of the completed module. At the top we can see a bank of switches, below that is a circuit board where we can see ICs, capacitors, and resistors.">
</a>
<figcaption>
Back
</figcaption>
</figure>
<figure class="horizontalTiles">
<a href="/images/synth/SequencerSide.jpeg">
<img src="/images/synth/SequencerSide.jpeg" loading="lazy" height="100px" alt="A photo of the completed circuit board showing two ICs, a power connector, and a large number of resistors, capacitors, diodes, transistors, and trimpots">
</a>
<figcaption>
Side
</figcaption>
</figure>
<div style="clear:both  ;">

</div>
<p>Using the sequencer is somewhat challenging as you have to set the pitch of each note – from
a range of 5 octaves or so – with just a single potentiometer. And the pitch
will drift as the components warm up! Analog synthesis has its advantages and
disadvantages!</p>
<h2 id="resources">Resources</h2>
<h3 id="instructions">Instructions</h3>
<ul>
<li><a href="https://www.ericasynths.lv/media/SEQ_MANUAL_v3.pdf">mki x es.EDU Sequencer User Manual</a></li>
</ul>
<h3 id="product-pages">Product Pages</h3>
<ul>
<li><a href="https://www.ericasynths.lv/shop/diy-kits-1/edu-diy-sequencer/">EDU DIY Sequencer</a></li>
<li><a href="https://www.ericasynths.lv/shop/diy-kits-1/mki-x-esedu-diy-system/">mki x es.EDU DIY System</a></li>
</ul>
<h3 id="community">Community</h3>
<ul>
<li><a href="https://www.modwiggler.com/forum/viewtopic.php?t=261156">Modwiggler thread</a></li>
<li><a href="https://modulargrid.net/e/erica-synths-edu-sequencer">Modulargrid page</a></li>
</ul>
<h3 id="simulations">Simulations</h3>
<p>These simulations are by Moritz Klein</p>
<ul>
<li><a href="https://tinyurl.com/2yk6obr5">Basic Op Amp Examples/Op Amp-Based Clock</a></li>
<li><a href="https://tinyurl.com/y8v7mrkr">Basic Gate to Trigger/Cut Out Negative Spike</a></li>
<li><a href="https://tinyurl.com/ycvhha6h">Scaled CV Output</a></li>
<li><a href="https://tinyurl.com/yatgsglm">Simple Individual Gates</a></li>
<li><a href="https://tinyurl.com/y6vasvt7">Comparators/Emitter Followers/The Failsafe Gate Output</a></li>
</ul>
<h3 id="videos">Videos</h3>
<ul>
<li><a href="https://www.youtube.com/watch?v=QN_JhVcCBvI">Introducing the mki x es.edu DIY sequencer kit</a> by Moritz Klein (5:51)</li>
<li><a href="https://www.youtube.com/watch?v=vHNQQ6yUGyo">Designing a simple 5-step sequencer from scratch</a>
by Moritz Klein (32:07)</li>
</ul>

<div class="info">
    
    Tags: <a title="All pages tagged &#39;synthesis&#39;." href="/tags/synthesis.html" rel="tag">synthesis</a>, <a title="All pages tagged &#39;diy&#39;." href="/tags/diy.html" rel="tag">diy</a>, <a title="All pages tagged &#39;electrical engineering&#39;." href="/tags/electrical%20engineering.html" rel="tag">electrical engineering</a>, <a title="All pages tagged &#39;sequencer&#39;." href="/tags/sequencer.html" rel="tag">sequencer</a>
    
</div>
]]></description>
    <pubDate>Thu, 05 Feb 2026 00:00:00 UT</pubDate>
    <guid>https://www.craigstuntz.com/posts/2026-02-05-building-a-synthesizer-14.html</guid>
    <dc:creator>Craig Stuntz</dc:creator>
</item>
<item>
    <title>Deployment Cadence</title>
    <link>https://www.craigstuntz.com/posts/2026-01-31-deployment-cadence.html</link>
    <description><![CDATA[<div class="info">
    Posted on January 31, 2026
    
</div>

<blockquote>
<p>“It was the best of times, it was the worst of times…”
-Charles Dickens</p>
</blockquote>
<p>My first professional software development job was producing statistical analysis
and simulations of physics experiments at the
<a href="https://www.bnl.gov/rhic/ags.php">AGS accelerator</a>. My deliverable was the
<em>output</em> of the software, not the software itself. Therefore, a “deployment”
meant “recompiling and executing the software on my local machine.” This was
awesome, in spite of the fact that I wrote the software in FORTRAN. My cycle
time was soooo low!</p>
<p>My second professional software development job was working for an
<a href="https://en.wikipedia.org/wiki/Independent_software_vendor">ISV</a> starting in
1999 (Y2K fixes!). We produced client/server software which other businesses
would install on their local networks. A deployment meant building the software
(during my stay there we went from building it on a developer’s local machine to
using a dedicated build system) and burning it onto a CD-ROM. After some testing
the CD-ROM would be mailed to customers (yes, through the postal system, and
accompanied by printed, bound manuals), who could
then (optionally!) install it on their systems. We did this roughly once per
quarter.</p>
<p>Decades later, these two jobs represent the fastest and slowest release cycles,
respectively, I’ve experienced.</p>
<h2 id="continuous-deployment">Continuous Deployment</h2>
<p>I will be using the phrase “continuous deployment” in this post to mean
“as soon as you push a commit, that change is integrated and deployed to
customers.” I want to call this out because it is different from continuous
integration or continuous delivery, which do not necessarily result in your
changes being immediately visible to customers.</p>
<p>The <a href="https://en.wikipedia.org/wiki/DevOps_Research_and_Assessment#DORA_Four_Key_Metrics">DORA metrics</a>
encourage us to optimize the deployment process as much as possible. Three of
the four metrics, “Change Lead Time,” “Deployment Frequency,” and
“Mean Time to Recovery” tend to improve if you can get code changes in front of
customers as quickly and as frequently as possible. For good reason, I think.
<a href="https://itrevolution.com/product/accelerate/">Others have written about this</a>,
so I won’t go into detail, but I do think that shipping software frequently
minimizes both the time to receive feedback and the overall impact of any bugs
in the new version. It forces you to think about how to make the overall
experience of an upgrade to a customer as non-disruptive as possible.</p>
<p>However, and this is what I <em>actually</em> want to examine in this post, the phrase
“<strong>as frequently as possible</strong>” is doing a lot of heavy lifting here. What does “as
frequently as possible” actually mean?</p>
<p>It depends.</p>
<h2 id="constraints-on-deployment-frequency">Constraints on Deployment Frequency</h2>
<p>Let’s assume, for the sake of argument, that your goal is to deploy as fast as
possible: You change a line of code, and you can instantly
test it in production. (Perhaps you disagree that this is a good idea, but I think
it does represent an extreme case of deployment speed, so it’s a <em>useful</em>
scenario to examine.) One big advantage of this model is that it forces you
to eliminate customer downtime in deployments.</p>
<h3 id="inefficiency">Inefficiency</h3>
<p>There are some things that tend to slow this down which we can handwave away as
inefficiency. For example, a compile/build/automated test/deploy step might
<em>take time,</em> but perhaps this could be greatly optimized? Some very lengthy
tests, such as fuzzing, can be run post-deployment or split into pre- and
post-deployment phases. I think “testing in
production” can actually be a very correct and practical thing to do in many
cases. It can be the only way that you find certain performance issues prior to
customers finding them. Depending on the scale of your production system, doing
a load test in a preproduction environment may not be representative of the
actual user experience in production. You can fix these with another update,
hopefully before a customer sees them.</p>
<h3 id="trust">Trust</h3>
<p>Other factors are harder to dismiss as inefficiency, though. Remember my second
job? The customers would decide when <em>or if</em> they wanted to install the update
on <em>their hardware.</em> We live in an age where vendors seem to want to control
which updates are installed on hardware we own, but I do cling to a somewhat
romantic notion that we should control our own devices. Now I may <em>choose</em> to
let the OS vendor install critical security patches without notifying me first,
but it is my choice. Similarly, you need to build a lot of trust with a customer
who has hundreds or thousands of employees working on software that you produce
before they will let you update it on your schedule, during their critical work.
If you tend to badly break
the software during an “upgrade,” the customer will want to minimize updates or
confine them to less critical times.</p>
<h3 id="complexity">Complexity</h3>
<p>Also, deploying to a customer environment might not be simple! I loved
<a href="https://www.youtube.com/watch?v=M-ZLz8Wg34s&amp;t=1s">this talk, “Update on Update,” by David Pacheco</a>
about how <a href="https://oxide.computer/">Oxide</a> is improving their update process. Their existing
process is essentially “an Oxide employee updates the software remotely.” The
process they want to move to is “a customer can download and run a self-service program which
will do the update.” <em>Both</em> of these scenarios currently involve downtime on the
rack, but they would like to minimize or eliminate downtime. Automating the
update, unsurprisingly, turns out to be a <em>very hard problem,</em> for reasons
which <em>mostly</em> boil down to “there are <em>a lot</em> of interdependent components on an Oxide
rack.” One other limitation discussed in the talk is that some of the production
Oxide racks are air-gapped; simply pushing software over the internet is not an
option for these. So there are two overall limits on deployment frequency here:
The first is that the upgrade process is not currently simple and Oxide is working very
hard to make it an “execute a script” experience. The second is that the Oxide
rack owners will still get to choose when to apply an update. Dave’s talk is
fantastic; I recommend you watch it!</p>
<h3 id="gates">Gates</h3>
<p>If you’re developing a native mobile app (or another app store with a review
process), deploying to an end user might be a
several weeks long back and forth between you and an app store reviewer in the
worst case, and <a href="https://www.runway.team/appreviewtimes">many hours on average</a>.
“Instant” deployment to a mobile user is simply not possible with a native app.
<a href="https://developer.apple.com/documentation/Xcode/About-Continuous-Integration-and-Delivery-with-Xcode-Cloud">Apple recommends continuous delivery</a> rather than
continuous deployment.</p>
<p>For other types of applications, even the hardware owner might not have the
option of saying “please update our systems
continuously.” I have worked in heavily regulated environments (utility billing)
where upgrades to some parts of the system needed to be reviewed by government
regulators before they could be deployed. This imposed a hard limit on
deployment frequency. I have heard similar stories about people who produce
software which is used in classified environments. In such cases you can
<em>discuss</em> increasing the deployment frequency with the gate managers, but it’s
never guaranteed that they will say yes!</p>
<h2 id="how-fast-is-too-fast">How Fast Is Too Fast?</h2>
<p>Above, I assumed that you are trying to deploy changes to production as fast as
possible: “You change a line of code, and you can instantly test it in
production.” I do not actually believe this is a good idea! In practice, I want
to, at a minimum, run a compiler, linter, unit tests, integration tests, security
scans, etc., before deploying. In other words, the standard integration process.
Before I do that, I’m going to manually test my changes locally, and in many
cases I will ask another developer to review them. This takes time, which I
think is time well spent!</p>
<blockquote>
<p>“Any observed statistical regularity will tend to collapse once pressure is
placed upon it for control purposes.” -<a href="https://en.wikipedia.org/wiki/Goodhart&#39;s_law">Charles Goodhart</a></p>
</blockquote>
<p>The danger with metrics, of course, is that you can pursue them to make the
“line go up” instead of looking at what you are actually trying to measure. I
do think that the DORA metrics are some of the most useful measurements that
one can make about a software project, but the results should be discussed,
rather than shown on a dashboard. Skipping peer review of important changes to
shave a few hours off of your Change Lead Time would be a bad trade, I think!</p>
<p>Similarly, some of the hours spent waiting for app store approval is Apple or
Google doing automated vulnerability scans on your apps. A developer can of course do
do this themselves, but experience has shown that not everybody bothers.</p>
<p>So when you hear phrases like “you should deploy changes to customers as fast
as possible,” I think it’s important to hear “as fast <strong>as possible</strong>” rather
than “<strong>as fast</strong> as …”</p>
<h2 id="one-size-fits-most">One Size Fits Most</h2>
<p>“One size fits all” clothes tend to be a compromise. They never fit as well as a
tailored garment. Similarly, I am suspicious of people who assure me
that continuous deployment is always possible or even desirable in <em>every
situation.</em> I think that continuous deployment is a <em>reasonable default position</em>
for starting a discussion about deployment, but I do not think that it is always
the best strategy. Continuous deployment can be useful as an “ideal” even in
cases where a huge (and perhaps insurmountable, given other business needs)
amount of work is necessary to make it happen.</p>

<div class="info">
    
    Tags: <a title="All pages tagged &#39;devops&#39;." href="/tags/devops.html" rel="tag">devops</a>, <a title="All pages tagged &#39;deployment&#39;." href="/tags/deployment.html" rel="tag">deployment</a>
    
</div>
]]></description>
    <pubDate>Sat, 31 Jan 2026 00:00:00 UT</pubDate>
    <guid>https://www.craigstuntz.com/posts/2026-01-31-deployment-cadence.html</guid>
    <dc:creator>Craig Stuntz</dc:creator>
</item>
<item>
    <title>Learning Computer Science Via Building Analog Synthesizers at CodeMash 2026</title>
    <link>https://www.craigstuntz.com/posts/2025-12-28-learning-cs-via-building-analog-synthesizers.html</link>
    <description><![CDATA[<div class="info">
    Posted on December 28, 2025
    
</div>

<p>Being as you are a devoted reader of my blog, I presume that you already know a
thing or two about computer science? But what if you didn’t know anything about
computer science? What if you decided to learn about it by
<a href="2023-02-20-building-a-synthesizer-0.html">building analog synthesizers</a>?</p>
<p>That’s the premise of <a href="https://events.codemash.org/2026CodeMashConference#/agenda?day=3&amp;lang=en&amp;sessionId=76186000008389209&amp;viewMode=2">my upcoming talk at CodeMash</a>.</p>
<blockquote>
<p>This presentation is an alternate history of your own journey learning
computer science. If you started learning all over again, and instead of
reading books on C# development or watching videos about Vue.js, you decided
to become a developer by building hardware analog synthesizers instead, what
would the programming world look like to you? Imagine that the first computer
you used was analog instead of digital, that you learned about abstraction
from op amps instead of base classes, and you had to solve problems with an
oscilloscope instead of a debugger. You might be very good at understanding
computational abstractions! Building an analog synthesizer from electronic
components is a fun way to learn about electrical engineering, but it also
holds many lessons about computer science. In this delightfully strange talk,
we will build several computational models from the transistors up, learn how
to debug from first principles, understand dynamic typing in terms of modular
synthesis, and also have a bunch of beeps, blorps, and solder.</p>
</blockquote>
<p>My goal is to have the strangest presentation at CodeMash. We will see if I
succeed. If I don’t, I hope I’m in the audience for the even weirder one.</p>

<div class="info">
    
    Tags: <a title="All pages tagged &#39;CodeMash&#39;." href="/tags/CodeMash.html" rel="tag">CodeMash</a>, <a title="All pages tagged &#39;presentations&#39;." href="/tags/presentations.html" rel="tag">presentations</a>, <a title="All pages tagged &#39;synthesis&#39;." href="/tags/synthesis.html" rel="tag">synthesis</a>
    
</div>
]]></description>
    <pubDate>Sun, 28 Dec 2025 00:00:00 UT</pubDate>
    <guid>https://www.craigstuntz.com/posts/2025-12-28-learning-cs-via-building-analog-synthesizers.html</guid>
    <dc:creator>Craig Stuntz</dc:creator>
</item>
<item>
    <title>Building a Synthesizer, Chapter 13: Building the VCF</title>
    <link>https://www.craigstuntz.com/posts/2025-12-27-building-a-synthesizer-13.html</link>
    <description><![CDATA[<div class="info">
    Posted on December 27, 2025
    
</div>

<div class="toc">
<ul>
<li><a href="2023-02-20-building-a-synthesizer-0.html">Introduction: The World of DIY Synthesizers</a></li>
<li><a href="2023-02-21-building-a-synthesizer-1.html">1: The mki x es.EDU DIY System</a></li>
<li><a href="2023-02-22-building-a-synthesizer-2.html">2: Building the Power Supply</a></li>
<li><a href="2023-03-02-building-a-synthesizer-3.html">3: Breadboarding the VCO</a></li>
<li><a href="2023-04-03-building-a-synthesizer-4.html">4: A Gentle Introduction to Op Amps</a></li>
<li><a href="2023-05-22-building-a-synthesizer-5.html">5: Building the VCO</a></li>
<li><a href="2023-08-11-building-a-synthesizer-6.html">6: The Logic Circuits Model of Computation</a></li>
<li><a href="2023-09-21-building-a-synthesizer-7.html">7: Building the Mixer</a></li>
<li><a href="2024-01-31-building-a-synthesizer-8.html">8: Building the Envelope Generator</a></li>
<li><a href="2024-02-21-building-a-synthesizer-9.html">9: A Field Guide to Oscillators</a></li>
<li><a href="2024-06-24-building-a-synthesizer-10.html">10: Building the VCA</a></li>
<li><a href="2025-04-07-building-a-synthesizer-11.html">11: Debugging Circuits and Software Debugging</a></li>
<li><a href="2025-09-09-building-a-synthesizer-12.html">12: Breadboarding the VCF</a></li>
<li>13: Building the VCF</li>
<li><a href="2026-02-05-building-a-synthesizer-14.html">14: Building the Sequencer</a></li>
<li><a href="2023-02-23-building-a-synthesizer-glossary.html">Glossary and Electrical Connections</a></li>
</ul>
</div>
<p>This is the second chapter discussing the VCF build; if you haven’t already read
the first, you might want to <a href="2025-09-09-building-a-synthesizer-12.html">start there</a>.
As a reminder, “VCF” stands for “Voltage Controlled Filter.” A VCF can be any
kind of filter; the mki x es.EDU VCF is just a lowpass filter. The “Voltage
Controlled” for this kit means that we can control the filter’s cutoff using a
control voltage signal, such as one produced by the
<a href="2024-01-31-building-a-synthesizer-8.html">envelope generator</a>. Using an envelope
generator allows us to vary the filter’s cutoff over the course of a note, which
makes it sound more interesting. Of course there are many, many more ways to use
a VCF!</p>
<h2 id="assembling-the-pc-board">Assembling the PC Board</h2>
<p>This turned out to be a fairly easy build. Everything worked correctly after
initial assembly. I made one mistake, which was accidentally melting two of the
capacitor housings a bit with my soldering iron (I work under a magnifiying lens,
and this was out of sight!):</p>
<figure>
<a href="/images/synth/VCFPCBoard.jpeg">
<img src="/images/synth/VCFPCBoard.jpeg" loading="lazy" height="300px" alt="The completed PC board. In the middle of the board there are three red capacitors next to each other, and the corner of these are melted a little">
</a>
<figcaption>
PC Board with Slightly Melted Capacitors
</figcaption>
</figure>
<p>One tip I found handy: Before installing the trimpot resistor, set it to the
middle of its range using your ohmmeter, because it’s hard to tell where it’s
set once it’s in the circuit. From there you can easily adjust it up or down as
needed. I found it needed not adjustment at all, however!</p>
<p>A couple of other views:</p>
<figure class="horizontalTiles">
<a href="/images/synth/VCFFinishedFront.jpeg">
<img src="/images/synth/VCFFinishedFront.jpeg" loading="lazy" height="300px" alt="A front view of the finished VCF, showing many knobs and jacks">
</a>
</figure>
<figure class="horizontalTiles">
<a href="/images/synth/VCFFinishedSide.jpeg">
<img src="/images/synth/VCFFinishedSide.jpeg" loading="lazy" height="150px" alt="A side view of the finished VCF, showing potentiometers, capacitors, ICs, and resistors">
</a>
</figure>
<div style="clear:both  ;">

</div>
<h2 id="matching-diodes">Matching Diodes?</h2>
<p>While I was building this PC board, I noticed something a bit strange at the
top. There were two spaces for diodes, for which no extra diodes were supplied in the
kit.</p>
<figure>
<a href="/images/synth/VCFDiodes.jpeg">
<img src="/images/synth/VCFDiodes.jpeg" loading="lazy" height="300px" alt="A photo of the PC board. There are two unpopulated spaces for diodes.">
</a>
<figcaption>
Missing Diodes?
</figcaption>
</figure>
<p>This was not mentioned in the directions, but I found reference to them on the
schematic:</p>
<figure>
<a href="/images/synth/VCFDiodesSchematic.png">
<img src="/images/synth/VCFDiodesSchematic.png" loading="lazy" height="300px" alt="A detail of the schematic, showing the diode testing circuit">
</a>
<figcaption>
Mystery… Solved?
</figcaption>
</figure>
<p>There are two links on the schematic, and they go to
<a href="https://makingcircuits.com/blog/diode-matching-circuit-pairing-circuit/">an article on diode matching</a>
and another on
<a href="https://www.fluke.com/en/learn/blog/digital-multimeters/how-to-test-diodes">how to measure diodes with your multimeter</a>.
The latter is clearly aimed at people who have never used diode mode on their
meter before. (No shade intended here! We all need to learn!) The former is sort
of interesting, but I can’t understand why this little circuit is on the board
at all, because,
<a href="https://modwiggler.com/forum/viewtopic.php?p=3862109&amp;sid=3fec989e512c1ea72847f75c828e1e43#p3862109">as Moritz notes</a>:</p>
<ul>
<li>Careful matching of diodes is not needed for this circuit</li>
<li>If you were going to build the circuit from the first link to carefully match
diodes, it would make a lot more sense to do it on a breadboard than to
solder the diodes onto a PC board temporarily!</li>
</ul>
<p>So, I didn’t bother to match my diodes. I guess it might make sense to do that
if I was trying to build the cleanest filter imaginable, but in this case I
prefer a kind of crunchy sound!</p>
<h2 id="finished">Finished!</h2>
<p>Here we have a square wave input and three possible settings of the filter: No
filtering at all, a low pass filter without resonance, and no filtering but a
“medium” amount of resonance:</p>
<figure class="horizontalTiles">
<a href="/images/synth/VCFFinishedNoFilter.jpg">
<img src="/images/synth/VCFFinishedNoFilter.jpg" loading="lazy" height="200px" alt="A square wave displayed on the oscilloscope">
</a>
<figcaption>
#NoFilter
</figcaption>
</figure>
<figure class="horizontalTiles">
<a href="/images/synth/VCFFinishedLowPass.jpg">
<img src="/images/synth/VCFFinishedLowPass.jpg" loading="lazy" height="200px" alt="The square wave with a low pass filter applied, which rounds off the sharp edges of the wave">
</a>
<figcaption>
Low Pass
</figcaption>
</figure>
<figure class="horizontalTiles">
<a href="/images/synth/VCFFinishedResonant.jpg">
<img src="/images/synth/VCFFinishedResonant.jpg" loading="lazy" height="200px" alt="The square wave with a low pass filter and resonance filter applied, which shows an up and down line at the start of each wave">
</a>
<figcaption>
Resonance
</figcaption>
</figure>
<div style="clear:both  ;">

</div>
<p>If you turn up the resonance still further you hear a high-pitched feedback.</p>
<p>Next, I connected the output of the Envelope Generator to the CV1 input of this
kit, which caused the cutoff to cycle back and forth between “no low-pass
filtering, but some resonance,” and
and “a low-pass filter with resonance” (this is a video, press play to see it):</p>
<video height="300" controls>
<source src="/images/synth/VCFCV.mp4" type="video/mp4">
</video>
<p>At this point you may be wondering how it sounds. Pretty nice! I want to put
some demos together, but I will save that for a future post. Hopefully soon!
However, it would be nice to have a CV source for the VCO to record a demo,
so first let’s <a href="2026-02-05-building-a-synthesizer-14.html">build the sequencer!</a></p>
<h2 id="resources">Resources</h2>
<h3 id="instructions">Instructions</h3>
<ul>
<li><a href="https://www.ericasynths.lv/media/VCF_MANUAL_v2.pdf">mki x es.EDU VCF User Manual</a></li>
</ul>
<h3 id="product-pages">Product Pages</h3>
<ul>
<li><a href="https://www.ericasynths.lv/shop/diy-kits-1/edu-diy-vcf/">EDU DIY VCF</a></li>
<li><a href="https://www.ericasynths.lv/shop/diy-kits-1/mki-x-esedu-diy-system/">mki x es.EDU DIY System</a></li>
</ul>
<h3 id="community">Community</h3>
<ul>
<li><a href="https://modwiggler.com/forum/viewtopic.php?p=3771212">Modwiggler thread</a></li>
<li><a href="https://modulargrid.net/e/erica-synths-edu-vcf">Modulargrid page</a></li>
</ul>
<h3 id="simulations">Simulations</h3>
<p>This simulation by Moritz Klein is the closest to the entire kit as built on the
PC board</p>
<ul>
<li><a href="https://tinyurl.com/23mp8quc">Resonance II</a></li>
</ul>
<h3 id="videos">Videos</h3>
<ul>
<li><a href="https://www.youtube.com/watch?v=wbG5lvBFCmA">Introducing the mki x es.edu DIY VCF kit</a>
by Moritz Klein (5:04)</li>
<li><a href="https://www.youtube.com/watch?v=eMODpxvdtvs">Erica.EDU Diode Ladder Filter Kit!</a> by Quincas Moreira (16:05)</li>
</ul>
<p>The folling series of videos are iterations on the design of what is ultimately
a slightly different VCF built by Moritz Klein. I do think they are useful,
though:</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=3tMGNI--ofU">DIY SYNTH VCF Part 1: Analog Filtering Basics</a> (21:45)</li>
<li><a href="https://www.youtube.com/watch?v=YNoj9Rrw_VM">DIY SYNTH VCF Part 2: Active Filters &amp; Resonance</a> (27:30)</li>
<li><a href="https://www.youtube.com/watch?v=ITJX9jP-zm4">DIY SYNTH VCF Part 3: Resonant High-Pass &amp; Vactrol-Based Voltage Control</a> (29:20)</li>
<li><a href="https://www.youtube.com/watch?v=jvNNgUl3al0">Designing a diode ladder filter from scratch</a> (36:03)</li>
<li><a href="https://www.youtube.com/watch?v=tWKLFcc_BJM">Turning my diode ladder filter into a eurorack module</a> (34:46)</li>
</ul>

<div class="info">
    
    Tags: <a title="All pages tagged &#39;synthesis&#39;." href="/tags/synthesis.html" rel="tag">synthesis</a>, <a title="All pages tagged &#39;diy&#39;." href="/tags/diy.html" rel="tag">diy</a>, <a title="All pages tagged &#39;electrical engineering&#39;." href="/tags/electrical%20engineering.html" rel="tag">electrical engineering</a>
    
</div>
]]></description>
    <pubDate>Sat, 27 Dec 2025 00:00:00 UT</pubDate>
    <guid>https://www.craigstuntz.com/posts/2025-12-27-building-a-synthesizer-13.html</guid>
    <dc:creator>Craig Stuntz</dc:creator>
</item>
<item>
    <title>Building a Synthesizer, Chapter 12: Breadboarding the VCF</title>
    <link>https://www.craigstuntz.com/posts/2025-09-09-building-a-synthesizer-12.html</link>
    <description><![CDATA[<div class="info">
    Posted on September  9, 2025
    
</div>

<div class="toc">
<ul>
<li><a href="2023-02-20-building-a-synthesizer-0.html">Introduction: The World of DIY Synthesizers</a></li>
<li><a href="2023-02-21-building-a-synthesizer-1.html">1: The mki x es.EDU DIY System</a></li>
<li><a href="2023-02-22-building-a-synthesizer-2.html">2: Building the Power Supply</a></li>
<li><a href="2023-03-02-building-a-synthesizer-3.html">3: Breadboarding the VCO</a></li>
<li><a href="2023-04-03-building-a-synthesizer-4.html">4: A Gentle Introduction to Op Amps</a></li>
<li><a href="2023-05-22-building-a-synthesizer-5.html">5: Building the VCO</a></li>
<li><a href="2023-08-11-building-a-synthesizer-6.html">6: The Logic Circuits Model of Computation</a></li>
<li><a href="2023-09-21-building-a-synthesizer-7.html">7: Building the Mixer</a></li>
<li><a href="2024-01-31-building-a-synthesizer-8.html">8: Building the Envelope Generator</a></li>
<li><a href="2024-02-21-building-a-synthesizer-9.html">9: A Field Guide to Oscillators</a></li>
<li><a href="2024-06-24-building-a-synthesizer-10.html">10: Building the VCA</a></li>
<li><a href="2025-04-07-building-a-synthesizer-11.html">11: Debugging Circuits and Software Debugging</a></li>
<li>12: Breadboarding the VCF</li>
<li><a href="2025-12-27-building-a-synthesizer-13.html">13: Building the VCF</a></li>
<li><a href="2026-02-05-building-a-synthesizer-14.html">14: Building the Sequencer</a></li>
<li><a href="2023-02-23-building-a-synthesizer-glossary.html">Glossary and Electrical Connections</a></li>
</ul>
</div>
<p>Nearly all synthesizers have a filter of some kind. There are many different
filter designs. Even a phrase like
<a href="https://www.youtube.com/watch?v=IRxeIEeAAnU">“the Moog ladder filter” can mean many different actual circuits.</a>
Differing filter designs can have as much to do with the character of how a synth
sounds as different oscillator shapes.</p>
<p>A VCF stands for a “Voltage Controlled Filter,” which is similar to a regular
equalizer filter except that it can be voltage controlled, which means that we
can use an Envelope Generator or a Low Frequency Oscillator to vary the
parameters of the filter (such as cutoff or resonance) as a note plays.</p>
<p>Moritz Klein’s goal when designing the mki x es.EDU VCF was to make a “classic
sounding” filter with a very simple design. And when you think about how simple
a low-pass filter can be – just a resistor and a capacitor, this feels like it
should be possible.</p>
<h2 id="breadboarding-the-filter">Breadboarding the Filter</h2>
<p>One really minor difficulty I had when breadboarding these was figuring out which
component was which, e.g., which jack was “input” and which was “output.” Here’s
the schematic, which is clear enough:</p>
<figure>
<a href="/images/synth/VCFFirstOrderSchematic.png">
<img src="/images/synth/VCFFirstOrderSchematic.png" loading="lazy" width="300px" alt="A schematic, from the manual, for the first order passive filter. There are no units on the resistor or capacitor.">
</a>
<figcaption>
Schematic
</figcaption>
</figure>
<p>However, the breadboard layout is quite a bit less clear, and the jacks are for
whatever reason never labeled in the manual. Quick, which jack is input?</p>
<figure>
<a name="breadboardLayout" href="/images/synth/VCFFirstOrderBreadboard.png">
<img src="/images/synth/VCFFirstOrderBreadboard.png" loading="lazy" width="300px" alt="A drawing, from the manual, of the breadboard layout of the first order passive filter">
</a>
<figcaption>
Breadboard Layout
</figcaption>
</figure>
<p>(It’s the one on the right.)</p>
<p>This becomes considerably harder when we start using seven op amps split
across two different ICs!</p>
<h3 id="passive-filter">Passive Filter</h3>
<p>Anyway, here’s the square wave I used as the input to the filter for my testing. You’ll
soon see why I need to show this by itself first.</p>
<figure>
<a href="/images/synth/VCFNoFilter.png">
<img src="/images/synth/VCFNoFilter.png" loading="lazy" width="300px" alt="A trace of a square wave on the oscilloscope">
</a>
<figcaption>
#NoFilter, Just a Square Wave
</figcaption>
</figure>
<p>First we build a passive, first order filter using just a resistor and a
capacitor. This does an OK job at filtering the output, but note that it also
distorts the input signal as well, because the resistor and capacitor add
impedance to the input side of the circuit, since there is no isolation between
the filter and the input:</p>
<figure>
<a href="/images/synth/VCFFirstOrder500.png">
<img src="/images/synth/VCFFirstOrder500.png" loading="lazy" width="300px" alt="The square wave, as a purple trace, and the output of a single order passive filter, as a yellow trace. This distorts the input as well as reshaping theo output.">
</a>
<figcaption>
First Order Passive Filter 500 Hz Signal Input
</figcaption>
</figure>
<p>The purple trace above is the input square wave (distorted due to the passive circuit
here), and the output of the filter is the yellow trace.</p>
<p>The filter doesn’t just reshape the wave; it also reduces the output at higher
input frequencies. Above I’m using a 500 Hz input; here’s with a 2 kHz input.</p>
<figure>
<a href="/images/synth/VCFFirstOrder2k.png">
<img src="/images/synth/VCFFirstOrder2k.png" loading="lazy" width="300px" alt="The same filter at 2 kHz. The output of the filter is much lower in volume than at 500 Hz ">
</a>
<figcaption>
First Order Passive Filter 2000 Hz Signal Input
</figcaption>
</figure>
<h3 id="second-order-passive-filter">Second Order Passive Filter</h3>
<p>Then we add a second resistor and capacitor to make a second order filter. The
output is much more like a sine wave with the second order filter. However, the
input is even more distorted.</p>
<figure>
<a href="/images/synth/VCFSecondOrder.png">
<img src="/images/synth/VCFSecondOrder.png" loading="lazy" width="300px" alt="A second order filter, which both turns the output into a smooth sine wave and also distorts the input into a more curved shape">
</a>
<figcaption>
Second Order Passive Filter
</figcaption>
</figure>
<h3 id="active-filter">Active Filter</h3>
<p>Next we build the active filter. Here’s the schematic:</p>
<figure>
<a href="/images/synth/VCAActiveSchematic.png">
<img src="/images/synth/VCAActiveSchematic.png" loading="lazy" width="300px" alt="A schematic for the second order active filter which has no units on the capacitors and units on only one of the three resistors">
</a>
<figcaption>
Active Filter Schematic
</figcaption>
</figure>
<p>When I first build this filter, it didn’t work. I got no output at all. After
some tracing, I finally noticed that we were supposed to replace the 1 µ
capacitors from the passive filter with 1 n capacitors for the active filter.
Unlike the real capacitors, which are visually very distinct, the “1 µ” and “1 n”
are just very similar looking <a href="#breadboardLayout">white blocks in the breadboard drawings</a>,
and the schematic immediately above omits their values altogether!</p>
<p>Anyway, with that sorted, I then had a respectable looking low-pass filter:</p>
<figure>
<a href="/images/synth/VCFActiveTrace.png">
<img src="/images/synth/VCFActiveTrace.png" loading="lazy" width="300px" alt="An oscilloscope trace from the active filter. The input is a square wave reprepresented in purple. The output is the yellow trace and is filtered, a bit, by the low pass filter ">
</a>
<figcaption>
Active Filter Input and Output
</figcaption>
</figure>
<h3 id="adding-resonance">Adding Resonance</h3>
<p>Next we make the filter resonant. We do this by feeding the output of the filter
back into the first capacitor, replacing its connection to ground with the
filter’s output.</p>
<figure>
<a href="/images/synth/VCFResonantTrace.png">
<img src="/images/synth/VCFResonantTrace.png" loading="lazy" width="300px" alt="Oscilloscope trace of resonant filter output. The yellow trace mostly hides the purple trace, except at the leading edge of the square wave where the output has a resonant overshoot ">
</a>
<figcaption>
Resonant Filter
</figcaption>
</figure>
<p>In the instructions and in his videos, Moritz notes
that this “is basically just the filter itself swinging at its set cutoff
frequency in addition and reaction to the oscillation it’s supposed to be
filtering.” That’s correct, but it’s only part of the story. In addition to the
filter “overreact[ing] to any sudden change in voltage at its input,” the filter
also introduces a delay in the signal as it passes through the filter and then
gets partially routed back into the input of the filter. The portion of the
output which is fed back into the filter circuit is out of phase with the input.
Both the delay/phasing and the amplification are responsible for the
characteristic sound of the resonance.</p>
<figure>
<a href="/images/synth/VCFResonantNoResonance.png">
<img src="/images/synth/VCFResonantNoResonance.png" loading="lazy" width="300px" alt="Oscilloscope trace showing the output at a lower level than the input, and the corners of the trace rounded off due to the low pass filter ">
</a>
<figcaption>
Adjustable Resonant Filter, Set to No Resonance
</figcaption>
</figure>
<figure>
<a href="/images/synth/VCFResonantWithResonance.png">
<img src="/images/synth/VCFResonantWithResonance.png" loading="lazy" width="300px" alt="Oscilloscope trace showing the output with the resonance level set to a higher level, causing an overshoot at the leading edge of the square wave   ">
</a>
<figcaption>
Adjustable Resonant Filter, with Resonance
</figcaption>
</figure>
<h3 id="adding-the-diode-ladder">Adding the Diode Ladder</h3>
<p>We want to make the filter adjustable using control voltage. The first step is
to make it adjustable at all. This kit solves that problem using diodes as
“voltage controlled resistors.” I won’t bother explaining the theory here as that
is very well covered in <a href="https://www.ericasynths.lv/media/VCF_MANUAL_v2.pdf">the manual</a>,
which I encourage you to read if this project is at all interesting to you.
The manuals for the mki x es.EDU DIY kits are (really) great reading.</p>
<figure>
<a href="/images/synth/VCFDiodeLadder.jpg">
<img src="/images/synth/VCFDiodeLadder.jpg" loading="lazy" width="300px" alt="A photo of the breadboarded VCF circuit">
</a>
<figcaption>
Filter with Diode Ladder At Upper Left
</figcaption>
</figure>
<h3 id="adding-the-output-stage">Adding the Output Stage</h3>
<p>In the images which follow, the input square wave is in yellow, the top and
bottom of the diode ladder are in purple and green, and the output is in cyan.</p>
<p>Some things to note here. First, the scaling is a bit deceptive. The input signal
is much hotter than the rest of the traces (note its vertical scaling is 2V/div).</p>
<p>The output signal varies quite a bit in amplitude depending on where the filter
cutoff is set, which isn’t ideal. We will fix that in the next section.</p>
<p>There’s a low-frequency noise, probably 60 Hz, in the output. I blame this on
the noisy breadboard and low output level.</p>
<figure class="horizontalTiles">
<a href="/images/synth/VCFOutputStage1.jpg">
<img src="/images/synth/VCFOutputStage1.jpg" loading="lazy" height="200px" alt="An oscilloscope trace showing an output with a fairly square wave signal, like the input">
</a>
<figcaption>
High Cutoff
</figcaption>
</figure>
<figure class="horizontalTiles">
<a href="/images/synth/VCFOutputStage2.jpg">
<img src="/images/synth/VCFOutputStage2.jpg" loading="lazy" height="200px" alt="An oscilloscope trace showing an output with a somwhat rounded signal">
</a>
<figcaption>
Middle Cutoff
</figcaption>
</figure>
<figure class="horizontalTiles">
<a href="/images/synth/VCFOutputStage3.jpg">
<img src="/images/synth/VCFOutputStage3.jpg" loading="lazy" height="200px" alt="An oscilloscope trace showing an output with an output which looks fairly close to a sine wave">
</a>
<figcaption>
Low Cutoff
</figcaption>
</figure>
<div style="clear:both  ;">

</div>
<p>This build took me quite a while to debug. At first I found that a couple of
wires were in the wrong breadboard sockets, and then after a considerable amount
of troubleshooting I found a couple of resistor leads which contacted each other due
to “leaning” on the breadboard. Which is, frankly, an easy mistake to make:</p>
<figure>
<a href="/images/synth/VCFOutputStageBreadboard.jpg">
<img src="/images/synth/VCFOutputStageBreadboard.jpg" loading="lazy" width="600px" alt="A photo of the breadboarded VCF circuit with output stage">
</a>
<figcaption>
VCF with Output Stage
</figcaption>
</figure>
<p>Still, this gave me ample opportunity to put
<a href="2025-04-07-building-a-synthesizer-11.html">my ideas about debugging circuits</a>
into practice!</p>
<h3 id="adding-cv-processing-and-output-scaling">Adding CV Processing and Output Scaling</h3>
<p>One part of breadboarding the mki x es.EDU kits which has repeatedly caused
confusion for me is, when going from one step to the next in the instructions,
figuring out which parts are not needed in the next step and should be removed
vs. which parts need to remain.</p>
<p>I think this example will show you what I mean. These two steps follow each
other sequentially in the manual.</p>
<figure class="horizontalTiles">
<a href="/images/synth/VCFBreadboardCV.png">
<img src="/images/synth/VCFBreadboardCV.png" loading="lazy" height="200px" alt="A hand-drawn picture of a breadboard with many components on it">
</a>
<figcaption>
“CV Processing” Step
</figcaption>
</figure>
<figure class="horizontalTiles">
<a href="/images/synth/VCFBreadboardResonance.png">
<img src="/images/synth/VCFBreadboardResonance.png" loading="lazy" height="200px" alt="Another hand-drawn picture of the same breadboard, with even more components on it, and a few removed">
</a>
<figcaption>
“Resonance II” Step
</figcaption>
</figure>
<div style="clear:both  ;">

</div>
<p>Quick, which components should I remove to go from the first to the second?
Which components need to be added? No matter how carefully I work I still make
mistakes. It’s another opportunity to test my debugging skills.</p>
<p>Anyway, after a lot of assembly and debugging, the output is much cleaner. Note
that the cyan trace here (the filter’s output) has been switched to 500 mV/div,
which is the same vertical resolution as the purple and green traces (which are
the top and bottom of the diode ladder), and 10* less magnification than the
50 mv/div vertical resolution of the cyan trace (output, again) of the previous
section.</p>
<figure class="horizontalTiles">
<a href="/images/synth/VCFCV2.jpg">
<img src="/images/synth/VCFCV2.jpg" loading="lazy" height="200px" alt="An oscilloscope trace showing an output with a fairly square wave signal, like the input">
</a>
<figcaption>
High Cutoff
</figcaption>
</figure>
<figure class="horizontalTiles">
<a href="/images/synth/VCFCV1.jpg">
<img src="/images/synth/VCFCV1.jpg" loading="lazy" height="200px" alt="An oscilloscope trace showing an output with with a somwhat rounded signal">
</a>
<figcaption>
Low Cutoff
</figcaption>
</figure>
<div style="clear:both  ;">

</div>
<p>I like this because you can really clearly see how the green and purple traces
get further away from each other as the filter’s cutoff gets higher and higher.
This “drags the input voltage” (into either end of the diode ladder) into a
different part of the diodes’ nonlinear range, making it a variable resistor,
which is what we wanted.</p>
<h3 id="resonance-ii">Resonance II</h3>
<p>The big problem that I had getting the resonance working is that I mistakenly
used the Schottky diodes supplied with the kit for the power supply in place of
the 1N4148 diodes that I was meant to use. The breadboard instructions just say
“diode,” although they are not interchangeable! Anyway, with the correct diodes
in place I can now affect the resonance a bit, although at this point every time
I touched anything I would knock off the input, one of my four oscilliscope
probes, a random electronic component, etc. I didn’t want to spend a lot of
effort adjusting the trimpot, which is fiddly enough without all of these other
cables to bump into. Still, I can see the effect of the resonance on the leading
edge of the square wave here, even if it is a bit subtle:</p>
<figure class="horizontalTiles">
<a href="/images/synth/VCFResonanceHigh.jpg">
<img src="/images/synth/VCFResonanceHigh.jpg" loading="lazy" height="200px" alt="An oscilloscope trace which shows the output wave with the resonance knob set high, showing a small spike at the head of the wave">
</a>
<figcaption>
High Resonance
</figcaption>
</figure>
<figure class="horizontalTiles">
<a href="/images/synth/VCFResonanceLow.jpg">
<img src="/images/synth/VCFResonanceLow.jpg" loading="lazy" height="200px" alt="An oscilloscope trace which shows the output wave with the resonance knob set low, showing a smooth round edge at the heaed of the wave">
</a>
<figcaption>
Low Resonance
</figcaption>
</figure>
<div style="clear:both  ;">

</div>
<p>Anyway, now we are finished with the breadboarding!</p>
<figure>
<a href="/images/synth/VCFBreadboardFinished.jpg">
<img src="/images/synth/VCFBreadboardFinished.jpg" loading="lazy" height="200px" alt="The completed breadboard">
</a>
<figcaption>
All Finished!
</figcaption>
</figure>
<p>The breadboard is by now getting quite crowded. I think this kit might be
better built across two breadboards.</p>
<p>This post is getting fairly long, so I think I will save the PC board build for
next time!</p>
<h2 id="resources">Resources</h2>
<h3 id="instructions">Instructions</h3>
<ul>
<li><a href="https://www.ericasynths.lv/media/VCF_MANUAL_v2.pdf">mki x es.EDU VCF User Manual</a></li>
</ul>
<h3 id="product-pages">Product Pages</h3>
<ul>
<li><a href="https://www.ericasynths.lv/shop/diy-kits-1/edu-diy-vcf/">EDU DIY VCF</a></li>
<li><a href="https://www.ericasynths.lv/shop/diy-kits-1/mki-x-esedu-diy-system/">mki x es.EDU DIY System</a></li>
</ul>
<h3 id="community">Community</h3>
<ul>
<li><a href="https://modwiggler.com/forum/viewtopic.php?p=3771212">Modwiggler thread</a></li>
<li><a href="https://modulargrid.net/e/erica-synths-edu-vcf">Modulargrid page</a></li>
</ul>
<h3 id="simulations">Simulations</h3>
<p>All of these simulations are by Moritz Klein</p>
<ul>
<li><a href="https://tinyurl.com/y5a8tc9l">Static cutoff/Variable cutoff</a></li>
<li><a href="https://tinyurl.com/y5x873gn">Two stage LPF</a></li>
<li><a href="https://tinyurl.com/y2fcoqsj">Active two-stage LPF</a></li>
<li><a href="https://tinyurl.com/y4j2khjd">Slightly resonant LPF</a></li>
<li><a href="https://tinyurl.com/2bfrjd36">Variable resonance</a></li>
<li><a href="https://tinyurl.com/y5z9m64u">Even number of stages/Odd number of stages</a></li>
<li><a href="https://tinyurl.com/2alfoolb">Downwards shift/Upwards shift/Driving the ladder</a></li>
<li><a href="https://tinyurl.com/2yowgjq8">The output stage</a></li>
<li><a href="https://tinyurl.com/2xp8ey77">Properly processed CV</a></li>
<li><a href="https://tinyurl.com/23mp8quc">Resonance II</a></li>
</ul>
<h3 id="videos">Videos</h3>
<ul>
<li><a href="https://www.youtube.com/watch?v=wbG5lvBFCmA">Introducing the mki x es.edu DIY VCF kit</a>
by Moritz Klein (5:04)</li>
<li><a href="https://www.youtube.com/watch?v=eMODpxvdtvs">Erica.EDU Diode Ladder Filter Kit!</a> by Quincas Moreira (16:05)</li>
</ul>
<p>The folling series of videos are iterations on the design of what is ultimately
a slightly different VCF built by Moritz Klein. I do think they are useful,
though:</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=3tMGNI--ofU">DIY SYNTH VCF Part 1: Analog Filtering Basics</a> (21:45)</li>
<li><a href="https://www.youtube.com/watch?v=YNoj9Rrw_VM">DIY SYNTH VCF Part 2: Active Filters &amp; Resonance</a> (27:30)</li>
<li><a href="https://www.youtube.com/watch?v=ITJX9jP-zm4">DIY SYNTH VCF Part 3: Resonant High-Pass &amp; Vactrol-Based Voltage Control</a> (29:20)</li>
<li><a href="https://www.youtube.com/watch?v=jvNNgUl3al0">Designing a diode ladder filter from scratch</a> (36:03)</li>
<li><a href="https://www.youtube.com/watch?v=tWKLFcc_BJM">Turning my diode ladder filter into a eurorack module</a> (34:46)</li>
</ul>

<div class="info">
    
    Tags: <a title="All pages tagged &#39;synthesis&#39;." href="/tags/synthesis.html" rel="tag">synthesis</a>, <a title="All pages tagged &#39;diy&#39;." href="/tags/diy.html" rel="tag">diy</a>, <a title="All pages tagged &#39;electrical engineering&#39;." href="/tags/electrical%20engineering.html" rel="tag">electrical engineering</a>
    
</div>
]]></description>
    <pubDate>Tue, 09 Sep 2025 00:00:00 UT</pubDate>
    <guid>https://www.craigstuntz.com/posts/2025-09-09-building-a-synthesizer-12.html</guid>
    <dc:creator>Craig Stuntz</dc:creator>
</item>
<item>
    <title>Building a Synthesizer, Chapter 11: Debugging Circuits and Software Debugging</title>
    <link>https://www.craigstuntz.com/posts/2025-04-07-building-a-synthesizer-11.html</link>
    <description><![CDATA[<div class="info">
    Posted on April  7, 2025
    
</div>

<div class="toc">
<ul>
<li><a href="2023-02-20-building-a-synthesizer-0.html">Introduction: The World of DIY Synthesizers</a></li>
<li><a href="2023-02-21-building-a-synthesizer-1.html">1: The mki x es.EDU DIY System</a></li>
<li><a href="2023-02-22-building-a-synthesizer-2.html">2: Building the Power Supply</a></li>
<li><a href="2023-03-02-building-a-synthesizer-3.html">3: Breadboarding the VCO</a></li>
<li><a href="2023-04-03-building-a-synthesizer-4.html">4: A Gentle Introduction to Op Amps</a></li>
<li><a href="2023-05-22-building-a-synthesizer-5.html">5: Building the VCO</a></li>
<li><a href="2023-08-11-building-a-synthesizer-6.html">6: The Logic Circuits Model of Computation</a></li>
<li><a href="2023-09-21-building-a-synthesizer-7.html">7: Building the Mixer</a></li>
<li><a href="2024-01-31-building-a-synthesizer-8.html">8: Building the Envelope Generator</a></li>
<li><a href="2024-02-21-building-a-synthesizer-9.html">9: A Field Guide to Oscillators</a></li>
<li><a href="2024-06-24-building-a-synthesizer-10.html">10: Building the VCA</a></li>
<li>11: Debugging Circuits and Software Debugging</li>
<li><a href="2025-09-09-building-a-synthesizer-12.html">12: Breadboarding the VCF</a></li>
<li><a href="2025-12-27-building-a-synthesizer-13.html">13: Building the VCF</a></li>
<li><a href="2026-02-05-building-a-synthesizer-14.html">14: Building the Sequencer</a></li>
<li><a href="2023-02-23-building-a-synthesizer-glossary.html">Glossary and Electrical Connections</a></li>
</ul>
</div>
<p>Sometimes you build a circuit and it doesn’t work correctly the first time. This
is especially common if it’s a new design, but even when building an existing,
known-good design it might be that your specific construction doesn’t function
correctly. No worries; it happens to the best of us! But you’re now staring at a
hunk of silicon which doesn’t do anything useful. How can you even begin to find
and fix the problem?</p>
<p>In the software world we call such an endeavor “debugging.” In the world of
electronic hardware, doing this falls under the umbrella of “analysis,” although
that also means understanding a working circuit, to a greater degree than
“debugging” means understanding working code. The electronics community has
informally adopted the term “debugging” for “what to do when the board you’re staring at
doesn’t work,” but I find it interesting that I own several books<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> on
electrical engineering and <em>none of them</em> have so much as a chapter on debugging
circuits! It seems like such a strange omission; I guess they presume that we
are learning this stuff in college and can just call a TA over if we get stuck?</p>
<p>It’s not that there is no interest in the topic; there are
<a href="https://amzn.to/4aORbSJ">whole books</a>
<a href="https://amzn.to/42NwRze">on the</a>
<a href="https://amzn.to/3Ep1qRt">subject</a>. But this sort of material just doesn’t seem
to be included in “introductory” texts!</p>
<h2 id="step-by-step">Step By Step</h2>
<p>I follow a similar process when debugging code or hardware:</p>
<ol type="1">
<li>Look for obvious problems</li>
<li>Have a model of the thing I’m looking at</li>
<li>Compare the observed behavior with the model</li>
<li>When I find a deviation of the observed behavior from the model, try making a
change</li>
<li>Observe if the change brings the behavior of the system closer to the model
and keep it or revert it as needed</li>
<li>If the circuit still does not work, go back to the step 2 and iterate until
I have a working system</li>
</ol>
<p>One difference between debugging software and hardware is that when I debug
software I am often writing new tests which run automatically. It’s not that
tests are impossible with hardware, and indeed I can see them being quite useful
on complex circuits, particularly if there’s a microprocessor available to run
them.
<a href="https://oxide-and-friends.transistor.fm/episodes/raiding-the-minibar">Testing harnesses are common</a>
when doing mass production of circuits. But
the circuits I work on are quite simple, nearly always “one off” (not mass
production) and there is no microprocessor available to run a test suite, so I
must unfortunately do without any tests other than manual analysis of the
circuit.</p>
<h2 id="look-for-obvious-problems">Look for Obvious Problems</h2>
<p>Without power, a circuit cannot work. It’s worth testing that the power rail
is connected and at the right voltage, and similarly for the ground. Also, make
sure that all parts of the circuit have the same “ground”; one problem I’ve
encountered myself is when an input to a circuit (such as an arbitrary waveform
generator) and the circuit itself have two different “grounds.” (The fix, in that
case, was to simply connect the two grounds together with a wire.)</p>
<p>If you turn on a circuit and a component incinerates itself in a puff of smoke,
that may give you a fairly good idea of where the problem lies! Similarly, if
a component is getting hot or smelly, well, unplug the power and take a look in
that neighborhood.</p>
<p>If you’re working on an old circuit it’s probably worth giving all capacitors a
visual inspection before you start any deeper analysis. Old capacitors are
<a href="https://antiqueradio.org/recap.htm">prone to failure</a> and can be tested in
circuit with an <a href="https://en.wikipedia.org/wiki/ESR_meter">ESR meter</a> or out of
circuit with a multimeter.</p>
<p>If this is a new circuit board that you’ve just built, a quick visual inspection
for cold solder joints can save hours of debugging.</p>
<h2 id="have-a-model">Have a Model</h2>
<p>Actually, have two: A logical model and a physical model. To be honest, I
usually have more than one logical model, but rather a tree structure of them.
“An analog synthesizer is composed of several
<a href="2023-03-02-building-a-synthesizer-3.html">VCOs</a>,
<a href="2024-06-24-building-a-synthesizer-10.html">VCAs</a>, VCFs… A VCO is
composed of an oscillator core, wave shaping circuits… Etc.” The physical
model says things like, “I expect the voltage drop across this resistor to be…”</p>
<p>Another way to look at this is that the logical model is the “top down” point
of view, and the physical model is the “bottom up” point of view. Both viewpoints
are needed to solve the problem.</p>
<p>This is similar to how I work in code. I have a logical and a physical model of
how the software should operate. The logical model is a high level interaction
between components or systems, and focuses more on the specific business problem
that the softare is solving. The physical model is at the functions and bytes
level. Some people argue that this is foolishness and I should just change the
code without thinking too much about it until my tests pass. I disagree with
that methodology!</p>
<p>Having a schematic is incredibly helpful with both types of models. A schematic
will often put the design into blocks, which helps you understand the logical
model, as well as listing specific components and voltages, which helps you
understand the physical model. Keeping a physical circuit mentally lined up with
a schematic can be quite tricky! To give one example, a physical circuit might
have an IC containing 4 different op amps, whereas the schematic will put those
4 op amps in 4 different places in the diagram.</p>
<h3 id="model-building">Model Building</h3>
<p>It can be difficult to keep a model in your head, so sometimes it’s helpful to
build a model. You can do this for example with a breadboard. Breadboards are
(electrically) noisy, but they allow you to make changes to a circuit nearly as
fast as you can change code.</p>
<p>Another tool which I find super-helpful in building a model of how a
circuit should work is a simulator. For the work I do,
<a href="https://www.falstad.com/circuit/">Falstad</a> is honestly all I’ve ever needed,
although it’s quite limited, supporting only a handful of components. There are
many more advanced tools, some of which are free, but Falstad has worked for me.</p>
<h3 id="noise">Noise</h3>
<p>One significant difference between debugging hardware and software is that with
hardware, noise must always be considered. Error correction in digital
computers is so good that although noise can and does disrupt circuits in a
computer, as software developers we rarely have to actually consider this.</p>
<p>But when working with hardware, noise is more than an occasional annoyance; it’s
the sea in which we swim. Noise is always present and often significant.</p>
<p>Sources of noise include:</p>
<ul>
<li>Poor connections on a breadboard</li>
<li>Signals reflected in an incorrectly-terminated cable</li>
<li>Less than “ideal” components</li>
<li>Radio frequency interference from nearby devices</li>
</ul>
<p>Sometimes people try to solve problems without having a model. They will say
things like “look for cold solder joints,” or “look at the power supply, and
look for shorts to ground,” or “look for shorted capacitors.”
That’s fine if it works – indeed, such advice probably comes from hard-won
experience – but there will be many problems which you can’t solve so easily!</p>
<h3 id="datasheets">Datasheets</h3>
<p>Electronics datasheets are a helpful tool for building your physical model of a
circuit. These are particularly important with ICs, because it is an
unfortunate fact that knowing the pinouts, signal voltages, etc., for one model
of IC tells you absolutely nothing about another, and getting, say, the power
pins of an IC incorrect can cook the IC when power is applied. If designing a
circuit from scratch, instead of building someone else’s design, I review the
datasheets for each active component I use 100% of the time, and I usually end
up reading them when building designs that other people have created, as well.</p>
<h2 id="compare-the-observed-behavior">Compare the Observed Behavior</h2>
<p>When we talk about comparing actual behavior with observed behavior, we are
probably talking about doing this comparison <em>at a specific part</em> of the circuit,
and it’s worth talking about how we will choose where to begin tracing.</p>
<p>It may be that the nature of the failure gives us some clue as to where to start
looking. If the circuit seems completely dead, perhaps the failure is towards
the beginning of the signal path?</p>
<p>But it might also be the case that there is no obvious place to begin looking.
In this case a good technique is to do a binary search. Start in the middle,
compare the signal with your model. If it seems correct, go halfway further
along the circuit. If it seems incorrect, back up halfway towards the beginning.
Repeat as necessary.</p>
<p>How do you know what the signal at some point in the circuit “should” be? It may
be that your mental model of the circuit is so good that you will just know.
But if that’s not the case, there are some other techniques you can use:</p>
<ul>
<li>Many circuits have “test points” on the PC board and documentation on what the
voltage/frequency should be at those labeled test points.</li>
<li>You can use a simulator to approximate what the reading should be.</li>
<li>You can “lower your standards” and just trace “am I getting any signal at all”
without thinking too hard about what it should be, specifically.</li>
</ul>
<p>Whenever your mental model differs from the physical circuit you’re building,
that’s a great place to look for issues. For example, your physical mental model
might include a pair of op amps in different points in a circuit, but your
breadboard might just have a single IC. The confusion this causes can be a source
of error – you might connect a wire to the wrong pin, or forget to connect the
power leads to the IC, which were omitted from your mental model.</p>
<h3 id="tools">Tools</h3>
<p>In order to compare the actual behavior of the circuit with the model which is
in your head or on paper, you need a way to see what is going on inside the
circuit. A “hardware debugger.” It turns out that there are many different kinds
of hardware debuggers.</p>
<p>Why not just have one tool which does it all?
<a href="https://digilent.com/shop/analog-discovery-3/">There are attempts at it!</a>.
But these tend to be featureless boxes which connect to a computer, and I find
them quite awkward to use. Having “one knob per function” is really helpful to
me when I’m working on a real circuit.</p>
<h4 id="multimeter">Multimeter</h4>
<p>So, at a bare minimum, I think you need a multimeter and an oscilloscope. A
multimeter measures voltage, current, and other things when they don’t change
very much. That is to say, if I want to measure a voltage which I expect to be
constant, then I will reach for my multimeter. I say “very much,” because of
course nothing is black and white and many multimeters have maximum/minimum
detection, inrush current measurement, etc. But as a general rule this is for
measuring things which are fairly steady. A multimeter is also great when you
need to measure something such as resistance or capacitance, which are not
generally directly measurable with an oscilloscope. Multimeters come in a few
different form factors, including the standard handheld “brick” style, a
benchtop style, and a current clamp style. All of these do generally the same
things, but may have different accuracy or a few specific features.</p>
<h4 id="oscilloscope">Oscilloscope</h4>
<p>An oscilloscope, by contrast, is all about showing how a signal changes over
time. The signal that you are measuring is <em>usually</em> voltage, although you can
buy current probes as well. Besides just displaying a trace of voltage over time
on the screen, even low-end oscilloscopes today will do frequency counting and
Fourier transforms. Even to just list the features of a contemporary low-end
digital oscilloscope would be a post by itself.</p>
<h4 id="audio-amplifier">Audio Amplifier</h4>
<p>I got a great idea from YouTuber
<a href="https://www.youtube.com/@hackmodular">Mitch</a>, which is
connect leads to an amplifier and a small speaker and use that in lieu of an
oscilloscope when tracing an audio signal through a circuit. This allows me to
hear the signal instead of see it, so that I can keep my eyes on the circuit.</p>
<h4 id="desoldering-tools">Desoldering Tools</h4>
<p>There is one other tool which I’ve found so handy while debugging physical
circuits that I’m leaning towards throwing it in the “essentials” category: Some
kind of desoldering tool. It’s <em>possible</em> to do this with a standard soldering
iron, but it’s so much more of a pleasant experience with a vacuum desoldering
iron or tool! You can buy a spring-loaded desoldering tool for about $10, so
at a bare minimum you want something like this, even if you can’t justify a
more expensive electric vacuum desoldering iron.</p>
<p>One of the reasons that having a really good desoldering tool is so handy is
that many parts cannot be tested in a circuit. To give one very simple example,
if you measure a resistor in circuit and you try to measure its resistance, you
may not get a correct measurement as there may be other resistors in parallel
to it. You must remove it from the circuit to get an accurate measurement.</p>
<h4 id="other-tools">Other Tools</h4>
<p>Beyond that, we get into “optional, but nice to have, depending on what you’re
doing” hardware. A power supply might seem like a must, although the circuit
you’re working on might already have one. A waveform generator is handy to
have around, and I’ve gotten a lot of mileage from mine while working on different
synthesizer modules, but I could have just used the VCO module that I built if
I didn’t have it.</p>
<p>I have a <a href="https://amzn.to/4i3sIuI">cheap component tester</a> which I can stick a
transistor, capacitor or diode into and it will identify the device, the pinout,
and a few relevant characteristics. I don’t use this very often. Going further
down this road, you might want an LCR or ESR meter if you are testing capacitors,
or a spectrum analyzer or vector network analyzer if you’re dealing with radio/RF
signals. But I have never needed them.</p>
<p>A logic analyzer is super-useful if you’re working on digital circuits, and
probably unnecessary if you’re not.</p>
<h3 id="testing-components-in-circuit">Testing Components “In Circuit”</h3>
<p>Software developers will be familiar with the distinction between a unit test
and an integration test; a similar distinction exists in hardware.</p>
<ul>
<li>In a <strong>unit test</strong>, we test a single function to determine if it returns the
expected result given some input. Similarly, we can test electrical components
<strong>in isolation</strong> (not as part of a circuit) to ensure that they are behaving
correctly. This is useful because the component cannot be affected by the
rest of the circuit, but it can be difficult if it’s necessary to un-solder a
component to do it.</li>
<li>In an <strong>integration test</strong>, we test an entire program to ensure that it
behaves correctly given some input. Similarly, we can test an entire
electrical circuit. Note that the test might occur at any point in the
circuit, not only at the output. But the important distinction here is that
the components of the circuit are connected together, and hence have some
influence on eath other’s behavior.</li>
</ul>
<p>In both cases, there are significant limitations on what we can test when real
hardware is involved. For example, you generally <em>cannot</em> measure the resistance
of a resisitor in circuit; the effect of the connected components might
overwhelm the individual value of one resistor. By contrast, some components
can’t exactly be tested by themselves; a 555 timer, for example, requires
external components to work.</p>
<p>So similarly to unit tests and integration tests in code, doing tests on
hardware “out of circuit” or “in circuit” gives us information about just one
component or the component in the context of a whole circuit. But whereas we
can generally test a function either with a unit test or an integration test,
there are some components which can really only be tested in circuit or out
of circuit, and it is up to the engineer to know the distinction, when it
occurs.</p>
<h2 id="make-a-change">Make a Change</h2>
<p>At some point you will identify a problem in the circuit and will have an idea
as to how to proceed. Perhaps you’ve found a bad solder joint, or perhaps a
wire has popped out of your breadboard? Perhaps you’ve discovered you used a
100 Ω resistor where you intended to use a 100 kΩ resistor.</p>
<p>Software is so malleable that we don’t really think much about how to change it;
we just type. But hardware can be more complicated. You might need to replace a
part (or just remove it for testing out of circuit), or perhaps cut a trace on a
PC board – or solder it back. Cutting a trace on a PC board is the equivalent
of “commenting out” code.</p>
<p>Go ahead and make that change.</p>
<h2 id="analyze-the-new-circuit">Analyze the “New” Circuit</h2>
<p>Before powering up the circuit, it’s worth thinking about the “new” circuit
you’ve built. Perhaps you think that now it will work, that this one change
you’ve made is all it will take to make everything work. And maybe that is so!
But it’s worth considering that when a circuit fails it can do so destructively,
so you might want to consider that and carefully look at the circuit for other
mistakes before powering it up again.</p>
<p>When you do power it up again, you should then decide if the change that you
made is <em>good.</em> Hopefully this is the case! But sometimes you will want to undo
the change.</p>
<h2 id="does-it-work">Does It Work?</h2>
<p>Hopefully the change that you made <em>improves</em> the circuit, but that doesn’t
always mean a complete fix. If the circuit now works, great, you’re done, you
can stop and consider what you have learned from the experience.</p>
<p>But if the circuit still is not fully functional, now it’s time to go back to
step 2, “Have a Model,” and work through the process again.</p>
<h2 id="grit">Grit</h2>
<p>Just as with debugging hardware, a significant factor in finding the solution to
a problem with hardware is grit: Your ability to stick to a problem and work
with it until you find the solution. You need a certain degree of persistence,
but not <em>too much.</em> Just as with software, you also need the ability to
recognize when you’re not getting anywhere and “call a friend.” I wish I had
better advice than just “don’t go too far in the direction of ‘not trying hard
enough’ or ‘trying too hard,’” but I will say that if you’ve been in software for
a while then you will recognize this problem and hopefully be good at judging
for yourself!</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>Specifically: <em>The Art of Electronics,</em> by Horowitz and Hill,
<em>Make: Electronics,</em> by Charles Platt, and
<em>Practical Electronics for Inventors,</em> by Paul Scherz and Simon Monk, plus
some titles specialized to building synthesizers.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>

<div class="info">
    
    Tags: <a title="All pages tagged &#39;diy&#39;." href="/tags/diy.html" rel="tag">diy</a>, <a title="All pages tagged &#39;electrical engineering&#39;." href="/tags/electrical%20engineering.html" rel="tag">electrical engineering</a>, <a title="All pages tagged &#39;computer science&#39;." href="/tags/computer%20science.html" rel="tag">computer science</a>, <a title="All pages tagged &#39;debugging&#39;." href="/tags/debugging.html" rel="tag">debugging</a>
    
</div>
]]></description>
    <pubDate>Mon, 07 Apr 2025 00:00:00 UT</pubDate>
    <guid>https://www.craigstuntz.com/posts/2025-04-07-building-a-synthesizer-11.html</guid>
    <dc:creator>Craig Stuntz</dc:creator>
</item>
<item>
    <title>Syntax Quirks in Vue.js</title>
    <link>https://www.craigstuntz.com/posts/2025-02-07-syntax-quirks-in-vue-js.html</link>
    <description><![CDATA[<div class="info">
    Posted on February  7, 2025
    
</div>

<p>The markup used in a Vue.js <code>.vue</code> file is a <em>melange</em> of JavaScript, TypeScript,
HTML, and a few other bits of punctuation thrown in for good measure. I
encountered one of the odd corners of this today, and as it took me an hour
to figure all of this out and
<a href="https://vuejs.org/guide/essentials/template-syntax#using-javascript-expressions">the documentation sort of hand waves around it</a>,
I thought I should write my notes down here.</p>
<p>Briefly, when you’re calling a method there are not consistent rules regarding
whether or not you should put <code>()</code> after the method name. Sometimes you <em>can</em>
do this, and sometimes you <em>cannot,</em> whereas other times the behavior of the
document will be silently changed depending upon whether or not you use them.</p>
<p>If you have this in a <code>template</code>:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> :disabled</span><span class="op">=</span><span class="st">&quot;foo&quot;</span><span class="dt">/&gt;</span></span></code></pre></div>
<p>… and you know that <code>foo</code> looks like:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>foo<span class="op">:</span> <span class="kw">function</span>() {</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> <span class="kw">false</span><span class="op">;</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>…is that template right or wrong?</p>
<p>Or what if the template looked like:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> :disabled</span><span class="op">=</span><span class="st">&quot;foo()&quot;</span><span class="dt">/&gt;</span></span></code></pre></div>
<p>Now is it right or wrong?</p>
<p>The answer is: “You don’t know if it’s right or wrong, because I haven’t told
you where <code>foo</code> is defined (in <code>computed</code> or in <code>methods</code>), and that matters
<em>a lot.</em> It also matters whether you place this function call in a <code>v-on</code> (<code>@</code>)
or a <code>v-bind</code> (<code>:</code>) portion of the markup.</p>
<p>All of the examples here are using functions which take no arguments, for
simplicity. Things get even more complicated when your function requires aguments!</p>
<p><a href="https://play.vuejs.org/#eNrFV1mPEz8M/ypmHhCIbfv/A09lWC7xABKHON5GQumMuxM2k4wSTw+hfneczJWW3aW7oOWhbezE9i++4v5IXtT1dNVgMk9SwqpWgvA00wBpIVdhwUsSC9VyW7JEUZymZPlTnuamqhvCYlYhlaZIZ8zz/NXE6IhYSD3u3bsPxoI29GxgMYGupWZeM6+8ldHowhTbgfQMG1GeLgYoLDxK9psxzZw7kwmkUvN5eB5+nmZJ+H3VKcmS2Sm4rSaxAbTW2Dk4RA8aHUwmB/reGwKhlFmzYGz4CCQtiHkhnXdzwTj6ZQzlWlqPMPv/FedDBG7R1ffu35qzIyxXefz2ELGl6wk8/LPADVV6ZF7uR+xdkL52Pl6Z5TfUeYTRR//eVW0i/W1n3UjrtTPt8bHuYyLqzkxF70VaD8e4SNDN+0M9PzVqVKRkZOOrk/oM7ip68ps+ffeMnoBF1yhyIDVkSSl0odBORV2rLUjn6xYELBudkzQ6S2BdooataYC2NbIQGeAXB7CQZOx0vFmE6CboQqR+xfdtVU33zt0A4y8IftfQIAAZbxAhEh2mQ7ELYYV2CIJYgS7QAskKgR0OkmChTH7uRhsBryZpEQqTNxWvYWlN1cnyBS739ReWbQcHwI3g2QTBlWatYS2pDJotIzQNKanZPT3SuGnDgv3CO0JVxhHECZ2jJSE1p4cXW5d8H+/qtdA0QMooo/3KY5PUWO1gKZTDE9giRTHxpvrzJ7DAXDQOw37bSwJIXLHP9pDwy4HFFPx9U6y6vsOWlsheyjGdMXMwLIBsQ+UWVkI1eLn3Xhp2kll664xhbez5nehMOgtlF97D44aQtuekC3vwLo6PIQ+QGxIWBUetMgUq1pKbRpNPvXaonLVTJbeIcdZMXW5lTbzCTW0sQYFLwYkJP7xIIUjMh+zjwTFwffJ6d/QUO9EbmsN/Jy1jF/J8F6h+Vpn3pw+vdpH+wUIIdFc1rT7giolKN5Lm0GqKVFAp3TQgm0yGwuv0chT31bbfbfDdgHXvLblI84MHB+AOn4ojbuS/dz4sQyhSR9uuhwcEQ2b02hbGcv1OcqP8aMSF2CoM2oKmTj45ScjlRi/l2fS7M5r/aAQNPjWqWnKT/lB737ksGe6cJWGWeht43k/dzVimxPz8Av53t/G8LPnITQ3tCrNk2OOUPEPOQb/9+vN7ztFok/O0UXz6is1P6Ixq2t7nj71swtsSnQto31Q+ebmhfXGvN4Ta9ZfyQMf4Zgn/2/Kpc9nVR7iPpuEFZnfukt1PXL8G+A==">There’s a playground here which will help as you read this.</a></p>
<p>From that playground we can derive this table, which I will explain below:</p>
<table>
<thead>
<tr class="header">
<th><code>computed</code>/<code>methods</code></th>
<th>() or not?</th>
<th><code>v-on</code> result</th>
<th><code>v-bind</code> result</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><code>computed</code></td>
<td>no ()s</td>
<td>Not allowed</td>
<td>Works correctly</td>
</tr>
<tr class="even">
<td><code>computed</code></td>
<td><code>()</code></td>
<td>Not allowed</td>
<td>Syntax error</td>
</tr>
<tr class="odd">
<td><code>methods</code></td>
<td>no ()s</td>
<td>Works correctly</td>
<td>Not what you want</td>
</tr>
<tr class="even">
<td><code>methods</code></td>
<td><code>()</code></td>
<td>Works correctly</td>
<td>Works correctly</td>
</tr>
</tbody>
</table>
<p>If your function is defined in <code>computed</code>, then using <code>()</code> will result in a
syntax error (even though you have written a function). This is at least
consistent and can be summarized concisely. Also, a function defined in
<code>computed</code> cannot be used in a <code>v-on</code> (<code>@</code>) binding.</p>
<p>When you write your function in <code>methods</code> things get much less clear. You can
use a <code>method</code> in either a <code>v-on</code> (<code>@</code>) or a <code>v-bind</code> (<code>:</code>) binding.</p>
<p>For a <code>v-on</code> binding, you are allowed to omit <code>()</code>, and the behavior
of the function will be exactly the same with or without it. That is:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> </span><span class="er">@</span><span class="ot">input</span><span class="op">=</span><span class="st">&quot;inputMethod&quot;</span><span class="dt">/&gt;</span></span></code></pre></div>
<p>…and:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> </span><span class="er">@</span><span class="ot">input</span><span class="op">=</span><span class="st">&quot;inputMethod()&quot;</span><span class="dt">/&gt;</span></span></code></pre></div>
<p>…will behave the same. This is <a href="https://vuejs.org/guide/essentials/event-handling#calling-methods-in-inline-handlers">“sort of” documented</a>.</p>
<p>However, with <code>v-bind</code> binding, if you omit the <code>()</code> then you are just
returning the method reference itself as a JavaScript expression, so if you have:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> :disabled</span><span class="op">=</span><span class="st">&quot;disabledMethod&quot;</span><span class="dt">/&gt;</span></span></code></pre></div>
<p>…and:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">input</span><span class="ot"> :disabled</span><span class="op">=</span><span class="st">&quot;disabledMethod()&quot;</span><span class="dt">/&gt;</span></span></code></pre></div>
<p>…then the top example will not invoke <code>disabledMethod</code> and will return the
value of the reference to <code>disabledMethod</code>, which is truthy if it exists. This
is probably not the behavior you want!</p>
<p>The bottom example will invoke <code>disabledMethod</code> and will use its result when
assigning the disabled attribute.</p>
<p>If any of this is unclear,
<a href="https://play.vuejs.org/#eNrFV1mPEz8M/ypmHhCIbfv/A09lWC7xABKHON5GQumMuxM2k4wSTw+hfneczJWW3aW7oOWhbezE9i++4v5IXtT1dNVgMk9SwqpWgvA00wBpIVdhwUsSC9VyW7JEUZymZPlTnuamqhvCYlYhlaZIZ8zz/NXE6IhYSD3u3bsPxoI29GxgMYGupWZeM6+8ldHowhTbgfQMG1GeLgYoLDxK9psxzZw7kwmkUvN5eB5+nmZJ+H3VKcmS2Sm4rSaxAbTW2Dk4RA8aHUwmB/reGwKhlFmzYGz4CCQtiHkhnXdzwTj6ZQzlWlqPMPv/FedDBG7R1ffu35qzIyxXefz2ELGl6wk8/LPADVV6ZF7uR+xdkL52Pl6Z5TfUeYTRR//eVW0i/W1n3UjrtTPt8bHuYyLqzkxF70VaD8e4SNDN+0M9PzVqVKRkZOOrk/oM7ip68ps+ffeMnoBF1yhyIDVkSSl0odBORV2rLUjn6xYELBudkzQ6S2BdooataYC2NbIQGeAXB7CQZOx0vFmE6CboQqR+xfdtVU33zt0A4y8IftfQIAAZbxAhEh2mQ7ELYYV2CIJYgS7QAskKgR0OkmChTH7uRhsBryZpEQqTNxWvYWlN1cnyBS739ReWbQcHwI3g2QTBlWatYS2pDJotIzQNKanZPT3SuGnDgv3CO0JVxhHECZ2jJSE1p4cXW5d8H+/qtdA0QMooo/3KY5PUWO1gKZTDE9giRTHxpvrzJ7DAXDQOw37bSwJIXLHP9pDwy4HFFPx9U6y6vsOWlsheyjGdMXMwLIBsQ+UWVkI1eLn3Xhp2kll664xhbez5nehMOgtlF97D44aQtuekC3vwLo6PIQ+QGxIWBUetMgUq1pKbRpNPvXaonLVTJbeIcdZMXW5lTbzCTW0sQYFLwYkJP7xIIUjMh+zjwTFwffJ6d/QUO9EbmsN/Jy1jF/J8F6h+Vpn3pw+vdpH+wUIIdFc1rT7giolKN5Lm0GqKVFAp3TQgm0yGwuv0chT31bbfbfDdgHXvLblI84MHB+AOn4ojbuS/dz4sQyhSR9uuhwcEQ2b02hbGcv1OcqP8aMSF2CoM2oKmTj45ScjlRi/l2fS7M5r/aAQNPjWqWnKT/lB737ksGe6cJWGWeht43k/dzVimxPz8Av53t/G8LPnITQ3tCrNk2OOUPEPOQb/9+vN7ztFok/O0UXz6is1P6Ixq2t7nj71swtsSnQto31Q+ebmhfXGvN4Ta9ZfyQMf4Zgn/2/Kpc9nVR7iPpuEFZnfukt1PXL8G+A==">try the playground</a>
, which will hopefully correct anything I’ve misstated.</p>

<div class="info">
    
    Tags: <a title="All pages tagged &#39;vue.js&#39;." href="/tags/vue.js.html" rel="tag">vue.js</a>, <a title="All pages tagged &#39;javascript&#39;." href="/tags/javascript.html" rel="tag">javascript</a>
    
</div>
]]></description>
    <pubDate>Fri, 07 Feb 2025 00:00:00 UT</pubDate>
    <guid>https://www.craigstuntz.com/posts/2025-02-07-syntax-quirks-in-vue-js.html</guid>
    <dc:creator>Craig Stuntz</dc:creator>
</item>
<item>
    <title>Building a Synthesizer, Chapter 10: Building the VCA</title>
    <link>https://www.craigstuntz.com/posts/2024-06-24-building-a-synthesizer-10.html</link>
    <description><![CDATA[<div class="info">
    Posted on June 24, 2024
    
</div>

<div class="toc">
<ul>
<li><a href="2023-02-20-building-a-synthesizer-0.html">Introduction: The World of DIY Synthesizers</a></li>
<li><a href="2023-02-21-building-a-synthesizer-1.html">1: The mki x es.EDU DIY System</a></li>
<li><a href="2023-02-22-building-a-synthesizer-2.html">2: Building the Power Supply</a></li>
<li><a href="2023-03-02-building-a-synthesizer-3.html">3: Breadboarding the VCO</a></li>
<li><a href="2023-04-03-building-a-synthesizer-4.html">4: A Gentle Introduction to Op Amps</a></li>
<li><a href="2023-05-22-building-a-synthesizer-5.html">5: Building the VCO</a></li>
<li><a href="2023-08-11-building-a-synthesizer-6.html">6: The Logic Circuits Model of Computation</a></li>
<li><a href="2023-09-21-building-a-synthesizer-7.html">7: Building the Mixer</a></li>
<li><a href="2024-01-31-building-a-synthesizer-8.html">8: Building the Envelope Generator</a></li>
<li><a href="2024-02-21-building-a-synthesizer-9.html">9: A Field Guide to Oscillators</a></li>
<li>10: Building the VCA</li>
<li><a href="2025-04-07-building-a-synthesizer-11.html">11: Debugging Circuits and Software Debugging</a></li>
<li><a href="2025-09-09-building-a-synthesizer-12.html">12: Breadboarding the VCF</a></li>
<li><a href="2025-12-27-building-a-synthesizer-13.html">13: Building the VCF</a></li>
<li><a href="2026-02-05-building-a-synthesizer-14.html">14: Building the Sequencer</a></li>
<li><a href="2023-02-23-building-a-synthesizer-glossary.html">Glossary and Electrical Connections</a></li>
</ul>
</div>
<h2 id="what-even-is-a-vca">What Even Is a VCA?</h2>
<p>If you have used keyboard/desktop style synthesizers but not modular synths,
you may have seen a “VCA” section of your synth before, or, depending on the
synth in question, you may have just seen a dial somewhere with a different
label.</p>
<p>A VCA allows you to use one control voltage to adjust the level of another
signal. The most common use is to use the output of an envelope generator to
adjust the level of the VCO’s output (turning an unchanging drone into a note
with an attack, decay, etc.). But there are many other uses for
a VCA!</p>
<p>Still, because the two uses above are so common, in many synthesizers the “VCA”
is represented as a “Volume” knob somewhere, or as a slider by an envelope
generator.</p>
<p>For example, here are two synthesizers with two different labels for the same
feature:</p>
<figure class="horizontalTiles">
<a href="/images/synth/VCAJupiter8.png">
<img src="/images/synth/VCAJupiter8.png" loading="lazy" height="300px" alt="The VCA section of the Roland Jupiter 8 synthesizer. It has a continuous slider marked Level and a four position switch marked LFO Mod">
</a>
<figcaption>
Jupiter-8 VCA
</figcaption>
</figure>
<figure class="horizontalTiles">
<a href="/images/synth/VCARetroSynth.png">
<img src="/images/synth/VCARetroSynth.png" loading="lazy" height="300px" alt="The two envelopes of Logic Pro's Retro Synth plugin. Beside each of the two envelopes is a slider marked Vol">
</a>
<figcaption>
Retro Synth
</figcaption>
</figure>
<div style="clear:both  ;">

</div>
<p>On the left is the “VCA” section of the Roland Jupiter-8. You get two controls:
A simple level slider, and a four position switch which controls how much one of
the synth’s LFOs affects the output. On the right is the Retro Synth plugin from
Logic Pro; here the “VCA” feature is just sort of implied by the envelope and
a slider labeled “Vel” (for Velocity) to the right of
the two envelope generators. Neither of these have a “control (voltage) input,”
because they are hard-wired to the envelope and the LFO, in the case of the
Jupiter-8, and to the envelopes, in the case of Retro Synth.</p>
<p>In the modular world it’s pretty common for a VCA to be a separate module, but
you may end up using them in exactly the same way: You want to turn the endless
drone of a VCO into distinct notes, so you connect an Envelope Generator to your
keyboard’s Gate output and you connect the output of the EG into the control
voltage input of a VCA. You then connect the VCO’s output into the audio signal
input of the VCA, and the output of the VCA now has distinct notes when you
press a key. Success!</p>
<p>However, a VCA can be used for other applications; I’d recommend watching
<a href="https://www.youtube.com/watch?v=xxr5fT-E7m0">this video</a> (particularly the
last two thirds) if you’d like to see examples.</p>
<h2 id="how-a-vca-works">How a VCA Works</h2>
<p>A VCA is simply a volume knob which can be controlled using a control voltage.
The control voltage is “…typically from -2.5 V to +2.5 V (5 Vpp) for LFOs, and
from 0 V to +8 V for ADSRs,” to quote the <a href="https://doepfer.de/a100_man/a100t_e.htm">Eurorack (non-)specification</a>.
There’s usually two “volume knobs,” in series: A <em>real</em> volume knob you can set
with your fingers and an electronically controllable volume setting you set with
a control voltage.</p>
<p>We know how potentiometers work, and the “real” volume knob is just one of
those, with an op amp for isolation and gain. But how does a control voltage
controlled volume knob work? If we ccould find such a thing as a “voltage controlled
resistor,” that would do the trick. It sounds like something which could be done
with an op amp, but it would require a somewhat less common circuit, called an
operational transconductance amplifier (or OTA) instead. These tend to have an
amplifier bias or <span class="math inline"><em>I</em><sub><em>A</em><em>B</em><em>C</em></sub></span> input which will control the gain of the amplifier
based on the current going into that input. But other designs are possible, and
as we will see the MKI x ES.EDU VCA is based on individual transistors instead
of an integrated circuit like an OTA.</p>
<h2 id="breadboarding-the-vca">Breadboarding the VCA</h2>
<p>Before I start breadboarding I ususally separate out the various resistor
values, set them in order, and tape them to a piece of paper with their values
in Ohms written alongside them.</p>
<div class="highlight">
<p><strong>Important</strong>: Before you start breadboarding, carefully look at the 10k, 20k, and
100k resistors. There are two different kinds! See the section
<a href="#resistance-is-futile">“Resistance is Futile,”</a> below, for details. The manual
does not include this distinction in the Bill of
Materials nor mention it at all unitl much later. You will want to keep these
two types of resitors separate, as it will make your life easier later on.</p>
</div>
<h3 id="breadboarding-the-volume-potentiometer">Breadboarding the Volume Potentiometer</h3>
<p>The first thing we are asked to build is a volume control using just a
potentiometer. Not exciting, but the idea is that a VCA will essentially be
just controlling volume using a control voltage instead of a knob. Of course the
truth is somewhat more complex, given the need to isolate modules from each
other in terms of impedance, but we’ll get there. Anyway, having built that
we can verify that we can indeed vary the volume of a signal passing through
our not-quite-yet-voltage-controlled-amplifier by twisting the potentiometer
knob. Moving right along…</p>
<h3 id="how-a-bipolar-junction-transistor-works">How a Bipolar Junction Transistor Works</h3>
<p>If you look up a <a href="https://en.wikipedia.org/wiki/Bipolar_junction_transistor">Bipolar Junction Transistor on Wikipedia</a>
or in many electronics books, the description will start with a discussion of
“P-type” and “N-type” semiconductors, and at the end you still won’t have a
good idea of what they do. Instead, I’m going to go with what Professor Aaron
Lanterman calls the “magic elves theory,” and describe what they do without
explaining the physical implementation, instead saying, “they do it that way
because the magic elves inside make it happen.” This is actually similar to
<a href="https://blog.adafruit.com/2015/06/17/the-original-full-size-negative-of-transistor-man-that-was-used-in-the-first-edition-of-art-of-electronics-photos-electronicsbook/">the “transistor man” presentation in Horowitz and Hill’s <em>The Art of Electronics</em></a>.</p>
<p>The purpose of these kits is perhaps more
about teaching you how electronics work than it is about building
the greatest synthesizer in the world. We’ve used a couple of transistors in
the past, but we haven’t gone very deep into how the work. So, no time like the
present! <a href="https://www.ericasynths.lv/media/VCA_MANUAL_FINAL.pdf">The instructions</a>
now take a (pages-long) digression into how transistors, and
bipolar junction transistors (BJTs) in particular, work. I won’t repeat that
here, but there is a points that I found confusing which I would like to try and
clarify.</p>
<p>In the VCO kit we used a transistor as a kind of variable resistor controlled by
the base input. The VCA instructions spend a good bit of time explaining why a
transistor is not, in fact, a resistor. A transistor provides resistance,
clearly: A fairly high resistance value when no voltage is connected to the base!
You can control the amount of current passing through the device using the
voltage you connect to the base.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>
<p>But resist<em>ance</em> is not the same as being a good resist<em>or</em>. A
transistor is designed to allow current to (easily) flow in only one
direction, unlike a resistor, and, also unlike a resistor, a transistor will
pass a fairly steady current regardless of voltage applied to the collector.
So the transistor is not a drop-in replacement for a resit<em>or</em>, even if it was
useful to us in supplying a
voltage-controlled <em>resistance</em> when building the VCO. It’s also noteworthy that
the VCO required a resistance which varied exponentially with the input control
voltage, and thus it’s super convenient to use a transistor, which more or less
does just this. With the VCA, however, we want the volume to change linearly
with the control voltage, so we will need to be careful about how we use the
transistors.</p>
<h4 id="the-problem-with-bjts">The Problem with BJTs</h4>
<p>One problem that BJTs have is that they are not very consistent devices. Even
BJTs from the same manufacturer with the same part number will vary in how much
current they pass and the precise base thresholds that control the transistor.
They will also vary considerably with the outside temperature.</p>
<p>So, we would like to compensate for these variations. How do we do that?</p>
<h4 id="negative-feedback">Negative Feedback</h4>
<figure class="inlineRight">
<a href="/images/synth/VCAManualNegativeFeedback.png">
<img src="/images/synth/VCAManualNegativeFeedback.png" loading="lazy" width="300px" alt="A transistor with the base connected to the input signal, the collector connected to the 12V rail through a 20k resistor, and the emitter connected to ground through a 10k resistor">
</a>
<figcaption>
From the mki x es.edu VCA Manual
</figcaption>
</figure>
<p>We start with a circuit much like the one on the right but without the 10k
resistor at the bottom. Then we get to the point that I found confusing!
Moritz talks about “introducing negative feedback” by connecting a resistor
between the transistor’s emitter and ground. That’s the 10k resistor at
the bottom of the circuit at right. What I didn’t understand was: A resistor is
a passive component; how could introducing just one resistor provide <em>any</em> kind of
feedback?</p>
<p>The answer is that although the resistor is passive, the transistor is very much
not passive, and putting a resistor between the emitter and ground makes a world
of difference because it changes the behavior of the transistor. How? When the
transistor’s emitter is connected directly to ground then <em>the emitter voltage will always be at
ground,</em> no matter what the base of the transistor is set to. But when there is
a resistor between the emitter and ground, things get more complicated.</p>
<p>A transistor is “active” when the voltage between the base and the emitter is
over a certain threshold, around 0.6V. When the transistor is active, current
will pass between both the base and the emitter and the collector and the emitter
(for an NPN transistor). This current flowing out of the emitter has to flow
across the 10k resostor. When a current flows across a resistor you get a
voltage. Therefore the voltage at the transistor’s emitter is no longer being
held at ground. Instead it varies depending upon the amount of current passing
through the resistor. And this change from “voltage is fixed at ground” to
“voltage varies with the amount of current being passed out of the emitter and
across the resistor” is the key to understanding how adding just that one
resistor can be called “negative feedback.” The more current that passes through
the resistor, the higher the voltage difference across its two ends will be,
right? However, this means that the end of the resistor connected to the emitter
will be at a higher voltage, and therefore the higher the current, the lower
the difference in voltage between the transistor’s base and emitter will be…
which reduces the current! This is the negative feedback.</p>
<p>Therefore, if something changes the gain of the transistor, such as its
temperature, this circuit will have the opposite effect: More amplification
means a higher voltage at the emitter which means a lower voltage from base to
emitter which means a lower gain!</p>
<h4 id="another-way-to-look-at-negative-feedback">Another Way to Look At “Negative Feedback”</h4>
<p>One of the things that confused me about the directions use of the phrase
“negative feedback” is that I hadn’t seen this phrase in other tutorials about
transistor amplification. But once I
understood what he meant by negative feedback I could line up what he was
saying with other explanations. So now I
will explain this in a different way, but I will be describing the same
“negative feedback” mentioned above. It helps me to have multiple ways to
consider the same phenomenon.</p>
<p>In this section I’m summmarizing the explanation from
<a href="https://www.mheducation.com/highered/product/practical-electronics-inventors-fourth-edition-scherz-monk/9781259587542.html"><em>Practical Electronics for Inventors, Fourth Edition,</em></a>
by Scherz and Monk.</p>
<p>The gain of a transistor, by definition, is:</p>
<p><span class="math display">$$ \begin{align} Gain = \cfrac {V_{out}} {V_{in}} = \cfrac {\Delta V_{C}} {\Delta V_{E}} \approx \cfrac {R_{C}} {R_{E}} \end{align} $$</span></p>
<p>Where <span class="math inline"><em>V</em><sub><em>C</em></sub></span> is equal to the voltage at the collector, <span class="math inline"><em>R</em><sub><em>C</em></sub></span> is equal to the
resistance at the collector, and similarly for the emitter. However, that’s not
quite the full story, because a transistor has a small internal
<a href="https://en.wiktionary.org/wiki/transresistance">transresistance</a>
in its emitter region, and one way you can think about thermal instability in a
transistor is that the transreistance changes with temperature. So a somewhat
more accurate model is:</p>
<p><span class="math display">$$ \begin{align} Gain \approx \cfrac {R_{C}} {R_{E} + r_{tr}} \end{align} $$</span></p>
<p>Where <span class="math inline"><em>r</em><sub><em>t</em><em>r</em></sub></span> is the transresistance value, which, again, is not constant. This
is not 100% accurate as there is some small current flowing from the base to the
emitter. But this does tell us that we can minimize the effect of <span class="math inline"><em>r</em><sub><em>t</em><em>r</em></sub></span> by
making <span class="math inline"><em>R</em><sub><em>E</em></sub></span> comparatively large relative to it.</p>
<p><span class="math display">$$ \begin{align} r_{tr} \approx \cfrac {0.026 V} {I_E} \end{align} $$</span></p>
<p>How much is that? Well, we have <span class="math inline"><em>I</em><sub><em>E</em></sub></span> <em>apporximately</em> 600 μA, which works out to
arount 46Ω. So a 10KΩ resistor should make its effects very small indeed. Scherz
and Monk say, “In practice, <span class="math inline"><em>R</em><sub><em>E</em></sub></span> should be chosen to place <span class="math inline"><em>V</em><sub><em>E</em></sub></span> around 1 V
(for temperature stability and maximum swing in the output). Here we’re around
6V, but note that in the very next step we will be reducing the voltage at the
emitter!</p>
<h3 id="breadboarding-a-one-transistor-vca">Breadboarding a One Transistor VCA</h3>
<p>So now let’s build a VCA (with actual voltage control, not just a
potentiometer). It looks like this:</p>
<figure>
<a href="/images/synth/VCAOneTransistor.jpg">
<img src="/images/synth/VCAOneTransistor.jpg" loading="lazy" width="600px" alt="A breadboard showing a simple, one-transistor VCA circuit. We can see a BJT, a DIP op amp package, a potentiometer, and a 1/8 inch jack.">
</a>
<figcaption>
Single Transistor VCA
</figcaption>
</figure>
<p>When we look at the output on a scope, though, the results are not so great:</p>
<figure>
<a href="/images/synth/VCAOneTransistorScope.png">
<img src="/images/synth/VCAOneTransistorScope.png" loading="lazy" width="600px" alt="An oscilloscope screen capture showing a yellow trace with a square wave, a magenta trace with the same square wave although much fuzzier and lower in amplitude, and a cyan trace which is the magenta trace inverted">
</a>
<figcaption>
Single Transistor VCA Output
</figcaption>
</figure>
<p>The yellow trace here is the output of a function generator I’m using as my
“signal.” This is like the audio signal I would be running through the VCA in
the real world. The magenta trace is the input into the base, and the cyan trace
is the VCA’s output at the jack. These last two look “blurry,” but note that the
vertical scale on the oscilloscope is much smaller for them. (20mV/div vs. 1V/div.)</p>
<h3 id="breadboarding-a-differential-amplifier">Breadboarding a Differential Amplifier</h3>
<p>There are a few problems with the circuit we have built so far:</p>
<ul>
<li>Poor amplification (low gain)</li>
<li>It’s noisy</li>
<li>The output is inverted</li>
<li>It’s still sensitive to temperature variation in the resistors, even though
we have improved things by adding the resistor at the emitter</li>
<li>When we change the CV signal, the DC offset of the audio output changes</li>
</ul>
<p>Moritz proposes to fix all of this by using a
<a href="https://electrosome.com/differential-amplifier-transistors/">differential pair</a>
of transistors. So first we add a second transistor and arrange both in a
differential pair.</p>
<figure>
<a href="/images/synth/VCATwoTransistors.jpg">
<img src="/images/synth/VCATwoTransistors.jpg" loading="lazy" width="600px" alt="A breadboard containing a cirucit with two transistors, a DIP chip with some op amps, a jack, a potentiometer, and a bunch of resistors">
</a>
<figcaption>
Two Transistor VCA
</figcaption>
</figure>
<p>Now we get two outputs, which are in opposite phase:</p>
<figure>
<a href="/images/synth/VCATwoTransistorsScope.png">
<img src="/images/synth/VCATwoTransistorsScope.png" loading="lazy" width="600px" alt="An oscilloscope screen capture showing a yellow trace with a square wave, a magenta trace with the same square wave although much fuzzier and lower in amplitude, a cyan trace which is output from one transistor and a green trace which is the output from another transistor">
</a>
<figcaption>
Two Transistor VCA Output
</figcaption>
</figure>
<p>Adding a couple more transistors and the second op amp (in the same IC package
as the first one), we can combine the two signals, subtracting them to produce
the difference between them (hence, differential pair). This is an op amp
configuration which I didn’t show in
<a href="2023-04-03-building-a-synthesizer-4.html">my op amp tutorial</a>, so here it is:</p>
<figure>
<a href="/images/synth/VCAOpAmpSubtractor.png">
<img src="/images/synth/VCAOpAmpSubtractor.png" loading="lazy" width="600px" alt="A schematic of a subtractor configuration of an op amp.">
</a>
<figcaption>
Subtractor
</figcaption>
</figure>
<p>If you’ve read that tutorial, you’ll understand how this works. It’s exactly the
same as an inverter configuration, except instead of having the non-inverting
(‘+’) input tied to ground, instead the non-inverting input of the op amp is
connected to another signal input, via a voltage divider. Without going into
too much detail in this post,<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a> it hopefully makes sense if you think that when
the non-inverting input is at ground this is exactly an inverter, because
0/ground minus the inverting input is the opposite of the inverting input.</p>
<p>Anyway, with the op amp outputting the difference of the two transistor signals,
our trace now looks like:</p>
<figure>
<a href="/images/synth/VCASubtractedTrace.png">
<img src="/images/synth/VCASubtractedTrace.png" loading="lazy" width="600px" alt="Oscilloscope showing a cyan trace in the foreground which is the subtraction of the two diode outputs">
</a>
<figcaption>
Subtractor Output
</figcaption>
</figure>
<figure class="inlineRight">
<a href="/images/synth/VCABreadboardFinished.jpg">
<img src="/images/synth/VCABreadboardFinished.jpg" loading="lazy" width="300px" alt="A breadboard with the completed VCA, which looks very much like the incomplete VCAs except for the addition of a few more resistors and a trimpot">
</a>
<figcaption>
Completed VCA
</figcaption>
</figure>
<p>The next step is to add a trimpot which allows adjustment to compensate for
small manufacturing differences between the two transistors. Even when both
transistors are at precisely the same temperature, two different transistors
of the same lot from the same manufacturer may vary in their gain. One way
around this is to buy a matched set of transistors in an IC package. Another way
around this is to add a trimpot:</p>
<figure>
<a href="/images/synth/VCAWithTrimpotTrace.png">
<img src="/images/synth/VCAWithTrimpotTrace.png" loading="lazy" width="600px" alt="Oscilloscope traces showing a now vertically centered output in cyan in the foreground">
</a>
<figcaption>
Subtractor with Trimpot
</figcaption>
</figure>
<p>Finally we change some resistor values to make the last op amp in the signal
chain boost the amplified signal up to a 10V peak to peak output.</p>
<p>I realize the signal looks quite noisy. Some of that is real, because
breadboards tend to be pretty noisy. Some of that is due to the high
magnification of the output signal vs. the function generator signal. Here it is
with the same vertical scaling:</p>
<figure>
<a href="/images/synth/VCASameVerticalScaling.png">
<img src="/images/synth/VCASameVerticalScaling.png" loading="lazy" width="600px" alt="Oscilloscope traces at equal verticl scaling; the output now appears less noisy compared to the input">
</a>
<figcaption>
At Equal Vertical Scaling
</figcaption>
</figure>
<h2 id="soldering-it-all-together">Soldering It All Together</h2>
<p>One curious fact about this project is that even though you are building
essentially two entirely separate VCAs, they are laid out differently on the
PCB. I’m not sure why this is the case; perhaps it has something to do with
where the sockets and potentiometers would have landed. But it’s necessary to
be very careful when selecting parts, because the layouts are <em>mostly</em> the same,
except where they’re different.</p>
<h3 id="resistance-is-futile">Resistance is Futile</h3>
<figure class="inlineRight">
<a href="/images/synth/VCAResistors.jpg">
<img src="/images/synth/VCAResistors.jpg" loading="lazy" width="300px" alt="A bunch of resistors, with 0.1% tolerance resistors on the right with just a tiny difference in shade from the 1% resistors on the left.">
</a>
<figcaption>
1% Resistors on Left, 0.1% Resistors on Right. Can you distinguish the light blues?
</figcaption>
</figure>
<p>Soldering on the resistors was unexpectedly complicated.</p>
<p>The manual notes that some of the resistors need to be closely matched, and
therefore they have supplied <em>a few</em> ±0.1% tolerance resistors where they need
to be as close as possible. This is really helpful, but it would have been even
more helpful if they had mentioned it prior to page 51 of the manual! As above,
I didn’t notice this difference when breadboarding and had to sort out the
differently specced resistors.</p>
<p>The manual states:</p>
<blockquote>
<p>You can identify these <strong>±0.1% tolerance resistors by their light blue bodies</strong></br>
[emphasis in original]</p>
</blockquote>
<p>There is a difference in the blue, but it’s quite subtle.
The ±0.1% and the ±1% resistors look near-identical, and I therefore had a bunch of
sorting parts to do from my breadboard assembly (made even more complicated by
the fact that 100Ω and 100kΩ resitors also look near-identical when you test them
with a multimeter, and their color codes aren’t that distinct, either).</p>
<p>Adding to the “light blue woes,” the colors in the photo in the manual (left,
below) don’t match the tolerances in the layout diagram (right)!</p>
<figure class="horizontalTiles">
<a href="/images/synth/VCAManualPhoto.jpg">
<img src="/images/synth/VCAManualPhoto.jpg" loading="lazy" width="400px" alt="This photo from the manual shows a bunch of resistors with different shades of blue, which don't match the tolerances specified in the parts diagram.">
</a>
<figcaption>
Photo from Manual
</figcaption>
</figure>
<figure class="horizontalTiles">
<a href="/images/synth/VCAManualDiagram.png">
<img src="/images/synth/VCAManualDiagram.png" loading="lazy" width="400px" alt="The same resistors in the parts layout diagram.">
</a>
<figcaption>
Diagram from Manual (Red Circles in Original)
</figcaption>
</figure>
<div style="clear: both;">

</div>
<p>A <strong>much better way of distinguishing these different kinds of resistors</strong>
(unmentioned in the manual!) is the
fact that <em>the resistor tolerance is encoded in the color band code.</em> So a 1%
tolerance 100k resistor will be “Brown Black Black Orange <strong>Brown</strong>” whereas a
100k 0.1% tolerance resisotor will be “Brown Black Black Orange <strong>Violet</strong>”</p>
<figure class="inlineRight">
<a href="/images/synth/VCATransistorTester.jpg">
<img src="/images/synth/VCATransistorTester.jpg" loading="lazy" width="100px" alt="A component tester with a transistor in it. The screen reads Transistor(NPN), hFE=536, Vbe=0.79V, Ic=7.4mA">
</a>
<figcaption>
Measuring a Transistor
</figcaption>
</figure>
<h3 id="choosing-transistors">Choosing Transistors</h3>
<p>Most of the rest of the soldering was uneventful, but when it came time to
attach the transistors I wanted to match them as closely as possible. I have a
component tester which I used to measure the hFE of all four transistors
included in the kit; their values were 524, 536, 552, and 572. So I paired the
524 &amp; 536 and the 552 &amp; 572 transistors.</p>
<h3 id="which-way-does-the-front-panel-go">Which Way Does the Front Panel Go?</h3>
<p>At one point the manual says to attach the front panel, but this kit has two
perfectly symmetrical VCAs and there are two possible ways to attach the front
panel! Only one way will result in the jacks going into their labeled spaces on
the front panel, though (with the “IN1” jack going into the hole labeled “IN1”),
so be careful and check the labels on the board when attaching this.</p>
<p>After attaching the front panel, the last step is to insert the two TL072 ICs.
I always get a little nervous when I do these, because if you insert them the
wrong way you will cook the ICs when you power them up. The two ICs are aligned
differently on the board, the photo in the manual is unclear, and the “notch” on
the IC doesn’t match the notch on the socket, so you will want to triple-check
this before you move on.</p>
<h2 id="powering-up">Powering Up</h2>
<p>Mortiz suggests first testing the VCA using an oscillator
(<a href="/posts/2023-03-02-building-a-synthesizer-3.html">which I have</a>) and
just seeing if the “Offset” potentiometers work correctly, so I did that, and
it sounded fine. Then you’re supposed to adjust the trimpots to center the
oscillation around ground. This is mentioned way back in the breadboarding
section of the manual, but it’s important to set your oscilloscope to DC coupled
when doing this adjustment. If the trace doesn’t move when you adjust the
trimpot then you probably have the oscilloscope input set to AC coupled.</p>
<p>I had to fix a cold solder joint. It took me longer to find than it should have.
Hint to future me: If something that used to work stops working, start your
search with the jack socket into which you’ve just plugged a cable. One thing that
confused me while I was fixing this was a feature I didn’t know the VCA had:
IN1 is normalled to IN2, so you can use one input for both VCA sections (and
similarly with CV1 &amp; CV2). This is mentioned in the manual, but only in the
appendix.</p>
<p>In the trace below, there is a sawtooth waveform from the VCO connected to IN1,
and nothing connected to IN2 (so the IN1 signal is normalled to the second VCA).
The Offset knobs are set to different positions. The yellow trace is OUT1 and
the purple trace is OUT2. I’m pleased that there is far
less noise in this (the PC board version of the VCAs) than there was in the
breadboard version.</p>
<figure>
<a href="/images/synth/VCANormalled.png">
<img src="/images/synth/VCANormalled.png" loading="lazy" width="600px" alt="Oscilloscope traces showing a purple trace and a yellow trace with the same signal at two different amplitudes">
</a>
<figcaption>
VCA Outputs
</figcaption>
</figure>
<p>With that finished, I now have a more or less functional synthesizer! There is
no filter (yet!), but I can play a note and have an envelope which sounds nice.
I’m very happy with the progress I’ve made!</p>
<figure class="horizontalTiles">
<a href="/images/synth/VCAFinishedFront.jpg">
<img src="/images/synth/VCAFinishedFront.jpg" loading="lazy" width="400px" alt="Photo of the front panel of the finished VCA. The single module has 2 VCAs. Each has an Offset potentiometer, and IN, CV, and OUT jacks">
</a>
<figcaption>
Front
</figcaption>
</figure>
<figure class="horizontalTiles">
<a href="/images/synth/VCAFinishedRear.jpg">
<img src="/images/synth/VCAFinishedRear.jpg" loading="lazy" width="400px" alt="Photo of the rear of the VCA. You can see a Eurorack power connector, blue trimpots, transistors, ICs for the op amps, capacitors, and a whole bunch of resistors">
</a>
<figcaption>
Rear
</figcaption>
</figure>
<div style="clear: both;">

</div>
<h2 id="resources">Resources</h2>
<h3 id="instructions">Instructions</h3>
<ul>
<li><a href="https://www.ericasynths.lv/media/VCA_MANUAL_FINAL.pdf">mki x es.EDU VCA User Manual</a></li>
</ul>
<h3 id="product-pages">Product Pages</h3>
<ul>
<li><a href="https://www.ericasynths.lv/shop/diy-kits-1/edu-diy-vca/">EDU DIY VCA</a></li>
<li><a href="https://www.ericasynths.lv/shop/diy-kits-1/mki-x-esedu-diy-system/">mki x es.EDU DIY System</a></li>
</ul>
<h3 id="community">Community</h3>
<ul>
<li><a href="https://modwiggler.com/forum/viewtopic.php?p=3771212">Modwiggler thread</a></li>
<li><a href="https://www.modulargrid.net/e/erica-synths-edu-vca">Modulargrid page</a></li>
</ul>
<h3 id="simulations">Simulations</h3>
<p>All of these simulations are by Moritz Klein</p>
<ul>
<li><a href="https://tinyurl.com/y8dbcu2q">Voltage Dividers</a></li>
<li><a href="https://tinyurl.com/y9kqbqnj">Transistor vs. Resistor</a></li>
<li><a href="https://tinyurl.com/ycdpkfnq">Transistor Amplifiers</a></li>
<li><a href="https://tinyurl.com/yd9lavjv">The Emitter Resistor</a></li>
<li><a href="https://tinyurl.com/y7ctexrd">Gain Changing Tricks</a></li>
<li><a href="https://tinyurl.com/yd7shjwz">The Differential Amplifier</a></li>
<li><a href="https://tinyurl.com/y7gxgq23">The Differential Amplifier (with Signal Subtraction)</a></li>
<li><a href="https://tinyurl.com/y7kh5c72">The Differential Amplifier (with Mismatch Fix)</a></li>
<li><a href="https://tinyurl.com/yce5ddsh">The MKI x ES.EDU VCA</a></li>
</ul>
<h3 id="videos">Videos</h3>
<ul>
<li><a href="https://www.youtube.com/watch?v=yMrCCx6uqcE">Designing a classic transistor-VCA from scratch</a>
by Moritz Klein (48:42)</li>
<li><a href="https://www.youtube.com/watch?v=NuS8slR2Ue0">Introducing the mki x es.edu DIY VCA kit</a>
by Moritz Klein (6:19)</li>
<li><a href="https://www.youtube.com/watch?v=xxr5fT-E7m0">MK1 vs es.EDU Dual VCA - Build and Demo</a>
by Quincas Moreira (31:04)</li>
</ul>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>Yes, I said <em>voltage.</em> bipolar junction transistors, despite
popular mythology,
<a href="https://www.youtube.com/watch?v=sIDD_GeF7eo">are voltage-controlled devices</a>.
What’s more, I’m not just being pedantic here; this will become important in the
discussion which follows.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p>Here’s <a href="https://www.youtube.com/watch?v=TBSvsbjAUWg">a nice tutorial on using op amps in a subtractor configuration</a>.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>

<div class="info">
    
    Tags: <a title="All pages tagged &#39;synthesis&#39;." href="/tags/synthesis.html" rel="tag">synthesis</a>, <a title="All pages tagged &#39;diy&#39;." href="/tags/diy.html" rel="tag">diy</a>, <a title="All pages tagged &#39;electrical engineering&#39;." href="/tags/electrical%20engineering.html" rel="tag">electrical engineering</a>
    
</div>
]]></description>
    <pubDate>Mon, 24 Jun 2024 00:00:00 UT</pubDate>
    <guid>https://www.craigstuntz.com/posts/2024-06-24-building-a-synthesizer-10.html</guid>
    <dc:creator>Craig Stuntz</dc:creator>
</item>
<item>
    <title>Building a Synthesizer, Chapter 9: A Field Guide to Oscillators</title>
    <link>https://www.craigstuntz.com/posts/2024-02-21-building-a-synthesizer-9.html</link>
    <description><![CDATA[<div class="info">
    Posted on February 21, 2024
    
</div>

<div class="toc">
<ul>
<li><a href="2023-02-20-building-a-synthesizer-0.html">Introduction: The World of DIY Synthesizers</a></li>
<li><a href="2023-02-21-building-a-synthesizer-1.html">1: The mki x es.EDU DIY System</a></li>
<li><a href="2023-02-22-building-a-synthesizer-2.html">2: Building the Power Supply</a></li>
<li><a href="2023-03-02-building-a-synthesizer-3.html">3: Breadboarding the VCO</a></li>
<li><a href="2023-04-03-building-a-synthesizer-4.html">4: A Gentle Introduction to Op Amps</a></li>
<li><a href="2023-05-22-building-a-synthesizer-5.html">5: Building the VCO</a></li>
<li><a href="2023-08-11-building-a-synthesizer-6.html">6: The Logic Circuits Model of Computation</a></li>
<li><a href="2023-09-21-building-a-synthesizer-7.html">7: Building the Mixer</a></li>
<li><a href="2024-01-31-building-a-synthesizer-8.html">8: Building the Envelope Generator</a></li>
<li>9: A Field Guide to Oscillators</li>
<li><a href="2024-06-24-building-a-synthesizer-10.html">10: Building the VCA</a></li>
<li><a href="2025-04-07-building-a-synthesizer-11.html">11: Debugging Circuits and Software Debugging</a></li>
<li><a href="2025-09-09-building-a-synthesizer-12.html">12: Breadboarding the VCF</a></li>
<li><a href="2025-12-27-building-a-synthesizer-13.html">13: Building the VCF</a></li>
<li><a href="2026-02-05-building-a-synthesizer-14.html">14: Building the Sequencer</a></li>
<li><a href="2023-02-23-building-a-synthesizer-glossary.html">Glossary and Electrical Connections</a></li>
</ul>
</div>
<p>If you own a synthesizer, or have worked with software synthesizer plugins, you
have no doubt encountered the term “VCO” or “Voltage Controlled Oscillator,”
and indeed
<a href="2023-03-02-building-a-synthesizer-3.html">we have seen it before in this series</a>.
Today most synthesizers are fully digital and just have “oscillators,” which produce
arbitrary waveforms using a computer, but you may also encounter “DCOs” or
perhaps just “Oscillators,” and wonder if these names actually mean
anything distinct.</p>
<p>In this post I’ll explain the “oscillator” part of how synthesizers produce
sound, look at three designs for actually building an oscillator, and talk about
the differences in sound that you may hear from these designs.</p>
<h2 id="voltage-controlled-oscillators">Voltage Controlled Oscillators</h2>
<p>I won’t spend a ton of time explaining how a VCO works because
<a href="2023-03-02-building-a-synthesizer-3.html">I’ve already done that</a>. For the sake
of this post I’ll just say that the entire oscillator, including both the
control of the tuning/timing as well as the shape of the wave produced, is analog.</p>
<p>The earliest commercial synthesizers,<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> such as Moogs, ARPs, and
Buchlas from the 1960s all used VCOs.</p>
<p>One of the major downsides of a VCO is you have to tune it. A lot. Some later
commercial synths with VCOs introduced an automatic tuning feature which helps
considerably, but they will still drift as you play them due primarily to
temperature changes but also due to aging of electronic componaents and the
inherent differences between two “identical” components. Two transistors from
the same manufacturer with the same model number and made in the same production
run can have very different electrical characteristics, for example. Also, as the name
suggests, they are <em>Voltage Controlled,</em> that is, controlled with an analog
voltage which might be produced by a keyboard or a sequencer, and there is
no single standard for control voltages, so you have to tune the VCO to produce
the correct note when you, for example, press keys on your keyboard.</p>
<p>On the other hand, VCOs often sound <em>amazing,</em> and the tuning drift may be part
of the reason! One surefire way to fatten up nearly any synthesizer sound is to
add another oscillator and <em>detune</em> the second oscillator so it doesn’t produce
precisely the same frequency as the first. Make it “fatter” still by using a
LFO (Low Frequency Oscillator) to vary the detuning over time just a bit. Our
ears hear this difference in frequencies as a beating sound
which might be pleasant or unpleasant, depending upon the amount of difference
between the two oscillators. Do this with two VCOs and there will be a certain
amount of drift between the two oscillators <em>as you play,</em> and this can be a
very pleasant sound. For a while, anyway, until it goes <em>way</em> out of tune and
you have to stop playing and retune it.</p>
<p>VCOs are said to have a characteristic sound due both to a limitation in the
waveshapes which are used (typically, square, sine, sawtooth/ramp, triangle,
and a few others) and also due to the variations in tuning. Happily, though,
many people really like the sounds that they produce.</p>
<h2 id="digitally-controlled-oscillators">Digitally Controlled Oscillators</h2>
<p>A DCO still produces an individual cycle of oscillation using analog electronics,
but uses a digital timer (typically controlled via a microcontroller) to trigger
each cycle. This allows the DCO to always have “perfect pitch.” Like a VCO, a DCO is also an
analog oscillator which uses analog circuits to produce the wave shape that you
hear.</p>
<p>A DCO will never require tuning. The note “A 440” will always sound at 440 Hz.
You can detune a DCO, which as above you might do if using two oscillators
together to produce a fatter sound. But there will not be any random “drift” in
the tuning.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a></p>
<p>The first synth with a DCO was the Roland Juno-6, which was available in
1982.<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a> DCO-based oscillators are not so common today. The majority of
commercial synthesizers are digital, and folks looking for an “authentic”
analog sound often want a VCO in spite of tuning headaches.</p>
<p>Essentially, the way a DCO works is the microcontroller sends a signal to the
analog circuit when it’s time to produce each cycle of the waveform. You can
think of a microcontroller sending a pulse every 1/440th of a second to produce
an “A” note. From there an analog circuit produces a single cycle in almost
exactly the same way that a VCO does, and then it stops and waits for the next
pulse from the digital controller.</p>
<p>I would explain how a DCO is implemented in hardware, but
<a href="https://blog.thea.codes/the-design-of-the-juno-dco/">Thea Flowers has already done a much better job of this than I ever could</a>, so if you’re interested (and it is <em>very</em> interesting, I think!)
then I recommend you go read her article!</p>
<p>Conceptually, the only “audible” difference between a DCO and a VCO is that the
DCO will never drift in the
tuning of its fundamental frequency. From a practical, real-world standpoint,
however, they might be implemented with different components and hence sound
very distinct, and even two “identical” analog oscillators can sound different
because of the difference between “identical” analog parts. Also, VCOs, as noted,
<em>do</em> drift in pitch, and this also makes them sound different than a DCO.</p>
<h2 id="digital-synthesis">Digital Synthesis</h2>
<p>Digital Synthesis produces a sound by using a computer program to produce
a waveform digitally. This includes samplers/sample players, all software
synthesizers, and “virtual analog” synths which use clever programming to try
to reproduce the sound of a VCO or DCO.
<a href="https://www.whippedcreamsounds.com/uhe-diva-review/">Some of them do it quite well!</a></p>
<p>The first mainstream digital synthesizers were <a href="https://www.youtube.com/watch?v=3TT5nAW8gi4">the Casio VL-1 in 1979</a>, which was
something of a toy but considerably cheaper than the other digital synthesizers
available at that time which were &gt; $10000 in 1980 dollars. Other notable
milestones in making digital synthesis a mainstream technique were the E-mu
Emulator, released in 1982, and the Yamaha DX-7, in 1983.</p>
<p>The sound of a digital “oscillator” will be precisely what its programmers
specify. This could be a perfect sine wave, playback of a sample,
a modeled recreation of an analog oscillator, small slices of a sample as with a
<a href="https://blog.native-instruments.com/what-is-wavetable-synthesis/">wavetable synthesizer</a>,
or many other things. A digital synthesizer will have a Digital to Analog
Converter (DAC) in its signal path; a VCO or DCO based synth will not require
this.</p>
<p>Some people will tell you that they can hear the digital “stepping” as, for example, a
16 bit, 44.1 kHz digital oscillator changes its output through its 65,536 possible
amplitudes over every 1/44100s. I will tell you that these people are wrong, and
that they cannot hear this.<a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a> However, they <em>may in fact</em> hear the
difference between a VCO and a “perfect” digital recreation of the “same” VCO,
because, once again, even two “identical” VCOs may sound different because of
differences between the analog parts, their variance with temperature, etc.</p>
<p>While it’s both theoretically and practically possible for a digital oscillator
to reproduce the sound of an analog oscillator well, there’s no denying that
many synth manufacturers have not bothered to do it well. Sometimes this is on
purpose; a wavetable synth, for example, is not <em>trying</em> to reproduce the sound
of a VCO. But even in cases where a synth manufacturer explicitly claims to
have an “analog sound” from their digital oscillators, well, some of them do
it better than others.</p>
<h2 id="synthesizers-today">Synthesizers Today</h2>
<p>The majority of hardware synthesizers, and <em>all</em> software synthesizers, sold
today are digital. Digital oscillators are quite common even in the modular
synthesis world. However, there are still fully analog hardware synthesizers
produced, in keyboard, desktop, and modular forms, generally with VCOs.</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>Yes, I’m aware that there were earlier synthesizers; the market
for “mass produced” syntesizers took off in the 1960s-early 1970s.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p>In principle it would be possible to make a DCO reproduce the sound of
a VCO exactly by programming the microcontroller to have VCO-like drift in its
tuning. In practice I’ve never seen a DCO-based synth do this, although it’s a
fairly common technique in “virtual analog” digital synths. Note also that
with any synth it’s quite common for preset designers to use the synth’s LFOs to
modulate the pitch a little (or a lot, as with vibrato), but this is a different
effect than the analog variations which are characteristic of a VCO and might
be emulated by a digital synthesizer. So in practice DCO-based synths sound
somewhat different from VCO-based synths, even if in principle they <em>could</em>
sound the same.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3"><p>I have seen claims online that the ARP Pro Soloist (1972) has a DCO,
including claims that it was <em>the first</em> commercial synth to use DCOs. I
have also seen schematics for the Pro Soloist online and as far as I can tell
these claims are incorrect. Never trust anything you find on the Internet, kids.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn4"><p>See, for example, the
<a href="https://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem">Nyquist theorem</a>
which shows that there is no difference between a properly dithered digital
recreation of an analog signal and the original analog signal at audible
frequencies, and before you tell me that your golden ears can hear sounds only
audible to dogs please consider that most synthesizer presets are low-pass filtered
down to frequences which all of us can hear. This is a complicated topic both
because there is in fact an actual audible difference between “identical” analog
VCOs as well as the metric tons of horse poop produced by the “audiophile” press
on the subject.<a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>

<div class="info">
    
    Tags: <a title="All pages tagged &#39;synthesis&#39;." href="/tags/synthesis.html" rel="tag">synthesis</a>, <a title="All pages tagged &#39;electrical engineering&#39;." href="/tags/electrical%20engineering.html" rel="tag">electrical engineering</a>, <a title="All pages tagged &#39;oscillators&#39;." href="/tags/oscillators.html" rel="tag">oscillators</a>
    
</div>
]]></description>
    <pubDate>Wed, 21 Feb 2024 00:00:00 UT</pubDate>
    <guid>https://www.craigstuntz.com/posts/2024-02-21-building-a-synthesizer-9.html</guid>
    <dc:creator>Craig Stuntz</dc:creator>
</item>

    </channel>
</rss>
