Gas flames on a brass pipe rise and dip with the standing sound wave inside.
A loudspeaker drives a 1.6 m pipe of propane, and a row of flames burns from holes along its top. The gas column is a real 1D acoustic simulation: pressure and velocity on a staggered grid of 256 cells, stepped 44,000 times per simulated second with a piston at one end, a rigid cap at the other and wall losses that grow with the square root of frequency. Each hole leaks gas by Bernoulli's law, so the program averages the square root of (gas pressure plus sound pressure) over the simulated waveform, and the flames follow that flow. At high gas pressure the flames dip at the pressure antinodes; at low gas pressure the holes choke for part of every cycle and the pattern flips, just as in real tubes. Below the pipe, the measured pressure envelope, a lock-in amplifier's slowed-down view of the wave and the exact frequency response with its numbered resonances.
Try it. Drag along the frequency strip (it snaps onto resonances) or drag sideways on the pipe to sweep. Up and down arrows jump between modes, left and right fine tune. Two tones (T) adds a second speaker tone to make the flames beat or to mix two modes, Low gas (G) flips the pattern, and Sound (S or click the pipe) plays the drive tone at the loudness the pipe rings. The autopilot tours the modes when left alone.
Paste this into Claude Code, Codex or any coding agent to get a simple version running, then take it wherever you like.
Build a Rubens' tube simulation with JavaScript and the HTML canvas element: a horizontal pipe with a row of gas flames whose heights follow the standing sound wave inside it. Put everything in a single index.html file with no libraries or build step, so I can open it directly in a browser.
Start simple:
- Make a full-window canvas, sharp on high-DPI screens. Draw a dark room and a brass pipe with about 50 holes along its top.
- Simulate the gas as a 1D wave: p (pressure, 200 cells) and u (velocity, 201 faces). Each step, u[i] -= (dt / dx) * (p[i] - p[i - 1]), then p[i] -= (c * c * dt / dx) * (u[i + 1] - u[i]), with c = 258 m/s (propane), a 1.6 m pipe and dt = 0.9 * dx / c. Drive u[0] = sin(2 * pi * f * t), keep the far end closed (u[200] = 0), and multiply u by (1 - 20 * dt) for losses. Run enough steps per frame to keep up with real time.
- At each hole, average sqrt(max(0, P0 + p)) over about 30 ms: Bernoulli's law for gas flow through a hole. Draw each flame as a teardrop whose height follows that flow.
- Add a frequency slider and print the resonances, f = n * c / (2 * L).
Once that works, make it beautiful:
- Draw flames with gradients (blue base, yellow core, fading orange tip) and globalCompositeOperation = "lighter", sway their tips with noise, and let them light the wall.
- Plot the RMS pressure under the pipe: flames dip at pressure antinodes when P0 is large. Try a small P0 to watch the pattern flip.
- Add a second tone a couple of hertz away and watch the flames beat.
Explain the key ideas in short code comments. When you're done, tell me how to open it and suggest three directions I could take it next, such as playing the drive tone with the Web Audio API, a plot of the pipe's frequency response, or a lock-in amplifier that shows the standing wave in slow motion.