Watercolor rain whose sound is built from first principles: ringing bubbles, not samples.
Most of the sound of rain on water comes from tiny air bubbles that drops trap under the surface, each ringing like a spring on a mass at the Minnaert frequency, about 3.26 kHz divided by its radius in millimetres. Every drop here decides whether it traps a bubble from its size (drops near 1 mm almost always do, the 14 kHz hiss of light rain; bigger drops sometimes trap bigger bubbles, the plinks), and each bubble is synthesized as van den Doel's damped sine whose pitch climbs as it rises, summed in JavaScript a few chunks ahead of the audio clock with thousands of unseen distant drops filling in the bed. The pond is a 2D wave equation with a little viscosity, lily pads cut out of it so ripples bend around them, shaded as pigment over a watercolor painting built from translucent layers of recursively deformed polygons. Puddles on paving add stone ticks, leaves are heavily damped low plates that collect water and drip from their tips, and the tin roof is per-pixel shaded corrugated steel whose drops excite inharmonic plate modes; the log in the corner plots every ring by time and pitch.
Try it. Tap to drop a single fat drop and see its bubble's radius and pitch; drag right for bigger drops and up for heavier rain. Click the scene chips or press 1 to 4 for the pond, puddles, leaves and tin roof; arrows change size and rate, Space drops a labelled drop, M mutes. Sound starts on the first click.
Paste this into Claude Code, Codex or any coding agent to get a simple version running, then take it wherever you like.
Build a rain simulation whose sound comes from physics instead of samples, using JavaScript, the HTML canvas element and the Web Audio API. 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, high-DPI canvas showing a blue-green pond from above.
- Run a 2D wave equation on a coarse grid (one cell per 4 pixels): next = (sum of 4 neighbours) / 2 - previous, times a damping of about 0.97. Shade cells by slope into a small offscreen canvas drawn scaled up.
- Spawn raindrops at random places as a Poisson stream; each pushes a small Gaussian dent into the grid, so rings spread and interfere.
Then make the sound:
- On the first click, create an AudioContext (show a "Click for sound" hint until then).
- Each drop that traps a bubble rings at the Minnaert frequency f = 3.26 / a Hz (a = bubble radius in metres). Give drops near 1 mm a tiny bubble (radius about 0.22 mm) most of the time, and bigger drops a random larger bubble now and then.
- Synthesize each bubble yourself as a damped sine whose pitch rises: damping d = 0.13 f + 0.0072 f^1.5 per second and f(t) = f0 (1 + 0.1 d t). Render 1024-sample stereo buffers slightly ahead of audioContext.currentTime and schedule them back to back.
Once that works, make it beautiful:
- Paint the scene like a watercolor: deform polygons by recursive midpoint displacement and stack 15 or so translucent copies, then multiply a paper-grain texture over everything.
- Add lily pads as holes in the wave grid so ripples bend around them.
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 thousands of unseen distant drops for a fuller bed of sound, a tin roof with inharmonic plate modes, or a live plot of every bubble's pitch.