Steer and focus a beam of sound with per-speaker delays in a live 2D wave simulation.
A column of loudspeakers fires the same tone with tiny delays, tau = (farthest distance minus own distance) / c, so every wavelet arrives at the focus in phase. The field is a real FDTD simulation of the 2D wave equation on about 60,000 cells, with a damping sponge on every edge so waves leave as if into open space. Color is the pressure, and brightness is the range-compensated time-averaged intensity, so wave crests stay visible while the beam glows. Space the speakers wider than half a wavelength and grating lobes appear, ghost beams where the path differences are whole wavelengths. For the polar plot, a lock-in amplifier extracts the complex amplitude of the tone on a line just in front of the array, and the Rayleigh-Sommerfeld integral carries it beyond the grid: to an arc through the focus, or to infinity for the true far field when steering, where it lands on top of theory from summed 2D Green's functions, side lobes and grating lobes included.
Try it. Drag anywhere to move the focus (or the steering direction in Steer mode). Use the sliders or the arrow keys for spacing and speaker count, click a speaker to switch it off, and toggle Pulses to watch individual wavefronts converge, Intensity for the time-averaged field, and Taper for Hann weights that lower the side lobes. Keys: S steer, P pulses, I intensity, T taper, A autopilot.
Paste this into Claude Code, Codex or any coding agent to get a simple version running, then take it wherever you like.
Build a phased array beamforming demo with JavaScript and the HTML canvas element: a column of speakers whose delayed outputs focus sound on a point you choose, simulated as real waves. 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. Simulate the 2D wave equation on a grid of about 300 by 190 cells with three Float32Arrays (previous, current, next): next = 2 * current - previous + S * S * (sum of the four neighbors - 4 * current), with S = 0.68.
- Add a damping layer about 20 cells wide on every edge (blend next toward zero more strongly near the border) so waves leave instead of bouncing back.
- Place 16 point sources in a vertical line near the left edge, half a wavelength apart (wavelength about 12 cells). Each step, add sin(omega * (step - tau_i)) to each source cell.
- Focusing: for a focus point F, set tau_i = (max_j |F - e_j| - |F - e_i|) / S, so every speaker's wave arrives at F at the same time. Let the user drag F with the mouse.
- Render the grid into ImageData on a small offscreen canvas with a diverging color map (teal negative, near-black zero, ochre positive), then drawImage it scaled up.
Once that works, make it beautiful:
- Keep a running average of pressure squared per cell and use it to set brightness, so the beam glows while the wave crests stay visible.
- Add a spacing slider in wavelengths. Past half a wavelength, grating lobes appear: draw their directions from sin(theta_g) = sin(theta_0) + m / (d / lambda).
- Draw a small delay bar next to each speaker so the focusing law is visible.
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 a polar plot of the beam compared with the analytic array factor, a pulsed mode that shows wavefronts converging, or a Hann taper to lower the side lobes.