A shaken dish of liquid locks into stripes, squares, hexagons and a 12-fold quasicrystal.
Above a threshold the flat surface of a shaken liquid goes unstable and standing waves organize into a lattice. The pattern comes from a Swift-Hohenberg style equation solved pseudo-spectrally on a 128 by 128 grid with hand-written real FFTs: a quadratic term makes hexagons, a filtered cubic term makes squares, and a second unstable wavenumber at 2 cos 15 degrees (the Lifshitz-Petrich model) locks twelve waves into a quasicrystal. Every pixel is then shaded like a photograph: its slope reflects a softbox and a bare lamp with Fresnel weighting, refracts the machined dish floor, and focuses light into caustics where the surface curves.
Try it. Turn the Forcing knob (or press 1 to 4) to change the pattern and the Amplitude knob (or the up and down arrows) to cross the threshold. Tap or drag in the dish to launch ripples and plant defects. Space shows the half-frequency swing in slow motion.
Paste this into Claude Code, Codex or any coding agent to get a simple version running, then take it wherever you like.
Build a simulation of Faraday waves, the standing-wave patterns that appear on a vibrating dish of liquid, with JavaScript and the HTML canvas element. 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 canvas that fills the window, stays sharp on high-DPI screens (scale by devicePixelRatio), and resizes with the window.
- Simulate the Swift-Hohenberg equation, du/dt = r u - (1 + laplacian)^2 u + g u^2 - u^3, on a 128 by 128 periodic grid. Write a small radix-2 FFT yourself and step it pseudo-spectrally: transform u, divide by (1 - dt * L(k)) where L(k) = r - (1 - k^2)^2 is the linear part, and add the nonlinear terms computed in real space. Scale k so a wavelength spans about ten cells.
- Start from tiny random noise and draw u as grayscale into ImageData, scaled up to fill a circle.
- With g = 0 you should get stripes; with g around 0.6 and r around 0.1, hexagons. Add a few keys to switch.
Once that works, make it look like liquid:
- Treat u as the height of the surface. Compute its slope with finite differences, build a normal per pixel, and color the pixel by how much it reflects a bright light up and to one side, so crests catch highlights.
- Add caustics: where the surface curves like a lens (negative Laplacian) brighten the dish floor seen through it.
- Let a click add a ring-shaped dent so the pattern has to heal, and let a slider move r below zero so you can watch the surface flatten.
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 the Lifshitz-Petrich model with a second wavenumber at 2 cos 15 degrees for twelvefold quasicrystals, a filtered cubic term for squares, or slow-motion sign flips to show the half-frequency response.