/ listening-for-chaos / double-pendulum-chaos
Sign in
listening-for-chaos / double-pendulum-chaos
double-pendulum-chaos / README.md
8.3 KBPreviewCodeBlameHistoryRaw

double-pendulum#

Interactive double pendulum, step 1 of "Listening for chaos". Work in progress.

Current state: the pendulum hangs at rest until Strike, which starts it from hanging rest with the energy on the slider (default 50 J), plus a random relative jitter of up to 1e-9 in each angle and momentum (so chaotic strikes differ from one another while regular ones sound alike); Pause freezes the animation. The Ghost toggle adds a second pendulum (translucent orange) started from the same state with the outer arm's angle offset by 10^-12 to 10^-1 rad (default 10^-10), restarted on Strike or Reset, with a readout of the angular separation and the time since the ghost started: it grows exponentially when the motion is chaotic and stays tiny when it is regular. Charts "Separation" (ghost separation, log scale, last 30 s) and "Lyapunov" (largest Lyapunov exponent over a 10 s window and since the strike, last 30 s). Parameters (masses, lengths, g, decay time, presets) fold out underneath. Toggle chips choose the plots: a spectrogram of the selected signal (frequency axis auto-scaled, dashed lines at the normal modes) and live traces of energies, energy balance error, angles, momenta and bob positions. The Poincare section panel (closed by default) has its own energy slider and uses the same masses and lengths, without damping: each time the inner arm swings down through vertical moving right, the outer arm's angle and momentum are plotted. 42 orbits start on a grid inside the allowed region (outlined) and leave 300 dots each, computed in a Web Worker and drawn as they arrive; regular orbits trace closed curves, chaotic ones scatter. Hover to pick out an orbit (with its Lyapunov exponent), click inside the outline to start a new orbit there (with "Click to strike" on, the default, this also strikes the pendulum above from that exact state, at the section's energy, without jitter). The animated pendulum's own crossings are drawn as dark rings (the latest larger), so you can see which orbit it is on; they clear on each strike. With "Follow" on, the section's energy tracks the pendulum's as it decays (recomputed after a 3% change, at most every 2 s), and optionally color orbits by Lyapunov exponent (log scale, blue regular to red chaotic). The Sound panel at the bottom starts closed; opening it switches the sound engine on (closing it switches it off). While it is open, every Strike plays a note: a separate, sped-up copy of the motion from the same strike; a damped note ends once it has decayed by 60 dB. The mute button (speaker icon) starts muted: notes are still computed and shown, just silent. The panel also has "Sound of" (the signal, also used by the animation spectrogram), volume, pitch (where the small-amplitude lower mode lands, 30 to 1000 Hz, default 100 Hz), and optional whole-note displays that do not scroll (strike at the left edge, end of the note at the right): a spectrogram of the sound, the largest Lyapunov exponent of the motion being heard (10 s window, physical time), its energy (from 0), and a scatter of lambda against energy accumulated over notes (colored by time into the note, earlier notes faded, Clear button). Spectrogram on by default. Hovering over any chart (animation or sound) shows a time cursor and the values there; a click pins it. Charts in the same card share the cursor. The time axis spans the predicted note length ((tau/2) ln 1e6 of physical time, divided by the speedup), is extended if the note runs longer, and is fitted to the note when it ends.

Live site: https://double-pendulum--listening-for-chaos.vault1-sites.magland.org/ (built and deployed by .github/workflows/pages.yml on every push to main).

npm install
npm run dev

Model#

Audio: a Web Worker (src/audio-worker.ts) integrates the audio copy with substeps of at most 3.2 ms of simulated time per sample, high-passes at 20 Hz, and streams 1024-sample chunks through a MessageChannel to an AudioWorklet (src/audio-worklet.ts) that plays them from a ring buffer, keeping about 0.25 s queued. The page shows how many times faster than real time the worker computes, and any dropouts. The worker also sends each chunk to the page, which feeds it to the audio spectrogram as the audio clock reaches it (2048-sample window, new column every 512 samples). The worker runs the same Lyapunov shadow on the note and sends one exponent and one energy value per 256 samples with each chunk; the page plots them in step with the spectrogram. The whole-note spectrogram keeps every column (pairs are averaged past 4096 columns) and draws only new pixel columns unless the axis or color scale changes.

Code: src/pendulum.ts (physics), src/sim.ts (running simulation with energy target), src/audio.ts (audio setup), src/signals.ts (audible quantities), src/lyapunov.ts (Lyapunov exponent), src/scatter.ts (lambda vs energy), src/cursor.ts (shared hover/pin cursor), src/poincare-core.ts, src/poincare-worker.ts, src/poincare.ts (Poincare section), src/audio-shared.ts (constants shared with the worker), src/traces.ts (strip charts), src/spectrogram.ts (spectrogram), src/main.ts (page).

↑↓ move↵openescclose