workshop private

← all creations

Low D

sprites · created 2026-10-03

200×142 pixel trumpet valve block in section with five loops. Each of the three loops is cut exactly right, and together they are 63.40 mm of tube short — 53.56 cents, a quarter tone and then some, on written low C#. The error is not an approximation of anything; it is precisely the four cross terms you throw away when you expand a product as a sum. And it cannot be engineered out: the flattest three tubes that exist are still 16.32 cents wrong, which is why the instrument has a ring on it instead.

physicssimulationpixel-artcanvas

Press one valve on a trumpet and the note is in tune. Press two and it is sharp. Press three and it is sharp by more than a quarter tone.

Every trumpet player meets this in the first month, as an instruction: pull the third slide for low D and low C#. Nobody is told why, and the why is not acoustics. It is that pitch multiplies and tube adds.

One valve is exactly right

A valve diverts the air through a loop and the horn gets longer. Dropping n semitones means multiplying the horn’s length by 2n/12, so the loop has to add

s(n) = 2^(n/12) − 1          times the horn it is being added to

Cut against the open horn, that is the whole design, and it is exact:

valvedropsfraction of the horntubeslide
2a semitone5.9463%87.51 mm43.75 mm
1a tone12.2462%180.21 mm90.11 mm
3a tone and a half18.9207%278.43 mm139.22 mm

(A slide has two legs, so it carries half the tube it adds. Every number in this file comes in both currencies and they are never the same number.)

The open horn here is 1.4716 m, and it is derived rather than looked up: a brass instrument’s playable modes go as n·c/2L for n ≥ 2, and a B♭ trumpet’s second mode is written C4 sounding concert B♭3, so c/L = 233.082 Hz. Against the ~1.48 m of tubing a trumpet actually contains, that is as close as an open pipe has any business getting.

Then you press two

Valve 1’s loop was cut against the open horn. Press valve 3 as well and the horn valve 1 is being added to is 18.9% longer than the horn it was cut for — so it is 18.9% too short, and there is nothing about a piston that could know otherwise.

fingeringshould dropwantshasshort bypullcents
2187.5187.51——0.00
12180.21180.21——0.00
33278.43278.43——0.00
1–23278.43267.7210.725.36+10.63
2–34382.50365.9416.568.28+15.53
1–35492.75458.6534.1017.05+30.32
1–2–36609.55546.1563.4031.70+53.56

Written low D is 1–3. Written low C# is 1–2–3. A quarter tone is 50 cents.

The error is four rectangles

Three valves down should multiply:

(1 + s₁)(1 + s₂)(1 + s₃) − 1

Three valves down actually add:

s₁ + s₂ + s₃

Subtract, and nothing is approximated away — what is left is exactly the terms a sum cannot carry:

s₁s₂ + s₁s₃ + s₂s₃ + s₁s₂s₃

termfractiontubeshare of the gap
s₁s₃2.31707 × 10⁻²34.098 mm53.78%
s₂s₃1.12508 × 10⁻²16.557 mm26.12%
s₁s₂7.28197 × 10⁻³10.716 mm16.90%
s₁s₂s₃1.37780 × 10⁻³2.028 mm3.20%
63.400 mm

That is the cross loop: a magnifier pointed at the gap in the bar, and the four rectangles dropped into it one at a time until they tile it. They tile it to the last bit of a double, and validate re-checks the identity on every render for all seven fingerings rather than taking three figures as proof.

It also says which way the error has to go. Every term is a product of positive numbers, so the shortfall is positive for every combination there is — a trumpet with two or three valves down is sharp, never flat, and there is no fingering and no instrument for which that is not true.

And the error is second order, which is the trap

s₁s₃ is 2.3% where s₁ is 12%. Second-order errors are the ones that stay invisible while you design the thing and then arrive all at once:

So the design is sound exactly as far as the intervals are small, and a trumpet needs six semitones from three valves.

The fix is a slide, and the slide is the design

The pull column above is not advice, it is the solution of shortfall / 2, and the two numbers in it are the two a player is taught:

17.05 mm for low D. 31.70 mm for low C#.

The pull loop draws it at the frame’s own scale — the loops under the casings and the bar in the panel are both at 220 px/m, so the slide coming out is the real 63.40 mm of tube — and then it sends the bill. Leave the slide where low C# needed it and the horn now has a third valve loop 63.40 mm too long:

with the slide left outcents
2–3 (written E♭4)−43.19
1–3 (written D4)−25.63
3 alone−61.61

Flat by nearly half a semitone, on the note directly above the one you just fixed. That is the second half of the pull loop, and it is the part that is actually hard to play: a chromatic run down from E4 wants the slide at

E4   1–2     0.00 mm
E♭4  2–3     8.28 mm
D4   1–3    17.05 mm
C#4  1–2–3  31.70 mm

Four consecutive semitones, three moves, the largest 14.65 mm between two adjacent notes. At 120 bpm in semiquavers that is 117 mm/s of left hand, which is why fast chromatic passages down there are simply played sharp.

You cannot cut your way out of it

Seven fingerings, three lengths. So: what is the best set of three tubes? optimiseLoops goes and finds out — Nelder–Mead on the weighted sum of squared cents errors, written out in the module rather than imported, because the point of the answer is that it was searched for.

Equal weight on all seven (lengthen every loop; spread the damage):

loop 1  +13.29 mm      1:−13.9   2:−5.8   3:−17.1
loop 2   +5.27 mm     12: −7.7  23:−5.7  13: +3.0   123:+23.0
loop 3  +17.38 mm      worst 22.99   rms 12.81

Worst case more than halved, from 53.56 to 22.99 — bought by making every single-valve note on the instrument six to seventeen cents flat. Those are the notes you play constantly. This is a worse instrument.

Minimax, the flattest possible worst case:

loop 1  +14.66 mm      1:−15.3   2:−14.0   3:−16.3
loop 2  +12.64 mm     12:−16.3  23:−11.8  13: +2.5   123:+16.3
loop 3  +16.58 mm      worst 16.32

16.32 cents. That is the floor. No three tubes exist that bring a three-valve instrument inside a sixth of a semitone, and the set that reaches it equioscillates — three fingerings pinned at ±16.32, which is what tells you the search found the bottom rather than a ledge. (The value is sharp; the lengths that achieve it are not unique, so the objective carries a tie-break.)

Weight it by what you actually play, and something falls out

The honest weighting is not “all seven equally” — it is how often each fingering is used. So CHART holds the standard chromatic chart from written F#3 to C6, thirty-one notes, and usageWeights counts it:

1:6   2:6   3:0   1–2:5   2–3:4   1–3:2   1–2–3:2

Valve 3 never appears on its own. Not once in thirty-one notes — the three-semitone drop goes to 1–2, and the third valve spends its entire working life in combination. Hand the optimiser that zero:

loop 1   +7.48 mm  (3.74 mm of slide)     1:−7.8   2:−1.4   3:−26.2
loop 2   +1.30 mm  (0.65 mm of slide)    12:+1.9  23:−10.6  13: −0.0   123:+23.4
loop 3  +26.67 mm (13.33 mm of slide)     rms over the chart: 8.82, against 19.08

It gives loops 1 and 2 under nine millimetres between them — those two carry the notes you actually play, so they have to stay near right — and it lengthens the third loop by 26.67 mm, throwing valve 3 alone 26 cents flat to buy back 1–3 (which lands on −0.04 cents, in tune) and more than half of 1–2–3. The one fingering it sacrifices is the one the chart had already given up on.

Which is the quiet joke in the whole instrument: the chart prefers 1–2 over 3 because the third valve is cut long, and the third valve can be cut long because the chart prefers 1–2. Both halves are load-bearing and neither is written down anywhere. The best loop is this set of three tubes, cycling all seven fingerings so you can watch the needle sit left of zero on everything a player uses and right of it only where the chart is not looking.

The loops

loopframeswhat it is
press8Valve 2 alone. Includes the two mid-stroke frames where the ports line up with nothing and the horn is simply shut — the half-valve, which is a real state and not a drawing shortcut.
stack81, then 1–3, then 1–2–3. The mechanism does not change; the needle walks 0 → 0 → +30.3 → +53.6.
pull831.70 mm of third slide, and then E♭4 with the slide still out at −43.2.
cross6The four rectangles, revealed one at a time, tiling 63.40 mm exactly.
best7The chart-weighted compromise loops, visibly longer, cycling every fingering.

The demo

Three valves on keys 1/2/3, both slides on sliders, all three loop lengths on sliders, and the four tunings as presets. The chips across the top are all seven fingerings live, so changing a loop shows you what it costs everywhere else at once.

It makes sound. Two voices: what the valves actually give you, and what you were aiming at. 53.56 cents on written C#4 is 247.22 Hz against 254.99 Hz, and the thing you hear is not “sharp”, it is a 7.77 Hz beat. Press 1–2–3, turn the reference on, then hit auto pull and watch the beat stop.

Worth doing on two partials: the same 53.56 cents on written F#3 an octave down beats at 5.18 Hz instead, because cents are a ratio and beats are a difference. A scale-free error with a scale-dependent symptom.

Reuse

source/low-d.mjs is framework-free and has no DOM above the drawing layer. The arithmetic is separable from the sprite:

import { centsOf, shortfall, slidePull, crossTerms, optimiseLoops,
         minimaxLoops, usageWeights, NOMINAL } from './low-d.mjs';

centsOf([0, 2]);                      // +30.315 — low D
slidePull([0, 1, 2]) * 1000;          // 31.70 mm of third slide
crossTerms([0, 1, 2]);                // the four, largest first
optimiseLoops(usageWeights());        // the compromise, found not asserted
minimaxLoops();                       // the floor: 16.32 cents

nelderMead(f, x0) is a general three-line-of-maths simplex search and knows nothing about trumpets. facts() is memoised and holds every number quoted above; node source/render.mjs regenerates every PNG and reprints all of them, so this file goes stale loudly rather than quietly.

validate() re-proves the load-bearing claims on every render: that each loop alone is exact, that the gap is the cross terms for all seven fingerings, that cents are scale-free, that a computed slide pull really does land the note on zero, that the minimax solution equioscillates, that the compromise is a genuine stationary point, that the chart really never uses valve 3 alone, and that a loop’s drawn depth and its share of the bar are the same number of pixels. node scripts/screenshot-demo.mjs boots the demo in a real browser and runs 22 assertions against it, four of which read pixels back out of the canvas rather than trusting the model that painted them.

Gotchas