# RESONANCE — asset provenance

Everything below was downloaded or made on 9–10 Oct 2026 for this concept. Nothing is hotlinked, nothing is
AI-generated, and there are no people in any image. The plate, the clamp, the stand, the studio sweep, the bow and the
sand are geometry and shaders written for this page; the figures are computed, not drawn.

## The physics (computed, not drawn)

| File | What | How |
|---|---|---|
| `js/figures.js` | The four figures: eigenmodes 27, 36, 69 and 97 of a free square plate (Kirchhoff, Poisson's ratio 0.3) clamped at its centre on an 8 mm washer | Ritz method with Legendre polynomials, N = 48 per direction (`tools/plate_modes.py`, `tools/export_figures.py`). Verified: the free plate reproduces Leissa's published frequency parameters (13.468, 19.596, 24.270, 34.801, 61.093…); mode shapes converged (correlation > 0.997 between N = 42 and 48); the same solver's modes 4 and 20, run through the same sand simulation, match photographs of real centre-bolted plates (`tools/refs/compare-physics-photo-vs-sim.png`). |
| `js/sand.js` | The sand: 52,000 grains (26,000 on phones, portrait screens and machines with four cores or fewer: the even-numbered subset, identical trajectories) | Grains hop where the plate's acceleration A·|W| exceeds a threshold (gravity), with a random kick proportional to the excess and a drift down the gradient of W²; below it they rock with the plate's tilt (which sets each line's width, about 4–5 mm) and creep to the nodal line; one grain in eight is fine dust that clings and migrates slowly (the haze real plates show between lines), and one in fourteen is an irregular grain that skids and lags behind as a stray. Seeded integer hashes and plain arithmetic only, so every browser computes the same figure (`window.__renderAt`). |

## The live plate (round 6, 11 Oct 2026)

Review note: the plate should keep vibrating on its own instead of following the scroll. The simulation that the
scroll used to scrub now runs on the clock: 50 steps a second (the rate the sand was
tuned at, about 150 bowed steps a stroke), the bow conducted by `js/live.js`, the sand by `js/sand.js` unchanged.
Scrolling and the four buttons only choose the figure. No video; no new assets.

- **The bow's rhythm.** Strokes of 2–4 s (bow speed 6.6–7.8 cm/s over 15–28 cm of hair), up-bow and down-bow in turn,
  each followed by a 0.45–0.9 s rest in which the hair lifts 1–2 mm and the plate rings down (0.9 a step). Per-stroke
  loudness 0.84–1.0; the first stroke in a new figure is full. All from seeded integer hashes (rule 40).
- **Re-tune.** A new ensemble stops the stroke in progress within 9 steps (0.18 s), the bow lifts about 3 mm and slides
  along the far edge to the new figure's bowing point (0.72 s, the page's one curve), touches down, and the next stroke
  builds over 0.7 s (a player finding the mode) and excites it. The sand migrates: asked for String trio at 6.0 s, the
  rendered frames' correlation with the trio reference still is 0.05 at 6.3 s, 0.46 at 8.1, 0.80 at 9.2, 0.86 at 10.6
  and 0.87 at 16 s with the bow still playing (other figures ≤ 0.22; `tools/settle.py`). In the simulation, 77% of the
  grains are on the trio's nodal lines after one stroke and 87% after three (`tools/live-sim.mjs`).
- **It keeps moving.** With no input, 3 s of the first view at 10 fps: the sand changes by 10.5/255 a frame on average
  (never under 4.0, which is the rest between strokes), 6.8% of the plate's pixels change by more than 6 levels each
  frame, and the kymograph shows the lines holding still while their grains shimmer (`shots/live-r6-kymograph.png`).
- **Cost and pacing.** At most 60 frames a second; the sand moves only on whole steps and the bow is interpolated
  between them. A late frame takes at most 4 steps and drops the rest of the debt, so the plate runs slow rather than
  lurching. Paused when the plate is off screen or the tab is hidden. Phones, portrait screens and machines with four
  cores or fewer run 26,000 grains. The frame rate is measured in flight (40 frames, or 2 s if slower); below about
  42 fps it steps down: the pixel ratio by 0.8 down to 1, then half the grains, then 30 fps, then a pixel ratio of 0.75
  (grains first if the main thread is the bottleneck). The first 2 s and 30 frames are not judged (shader compile, the
  canvas fading in). Measured here without drawing (`?simonly`, two shared cores, load average 6–7): 52,000 grains run
  at 53–60 fps with simulation time tracking the clock at 0.95 and 5.1 ms of main-thread work a frame; 26,000 at 60 fps,
  1.00, 2.7 ms. Drawing here is SwiftShader (software GL, 10–15 s a frame), so the GPU side could not be timed: the loop
  stepped itself down to 30 fps and ran slow with sand and bow together, as designed.
- **Tests.** `window.__renderAt(t, { k })` or `{ schedule: [[seconds, k], ...] }` replays the plate from the first frame
  for t seconds of simulation; `?renderAt=t&k=` pins it at load; `__live()` resumes; `__liveStats()` reports frame times,
  steps, downshifts and every re-tune as `[step, figure]`, so a live session can be replayed exactly.

## Code

| File | Source | Licence | Notes |
|---|---|---|---|
| `vendor/three-0.185.1/three.core.min.js`, `three.module.min.js`, `LICENSE.txt` | npm `three@0.185.1`, `build/` (copied from the Marbre concept's vendored copy) | MIT | Pinned, unmodified. The licence file renamed `.txt` for the host. |
| `js/resonance.js` | written for this concept | — | Scene, materials, plate-space sand height maps, analytic soft shadows of the bow, studio environment, depth of field, print grade. |
| `js/bow.js` | written for this concept, after Marbre's modelled cello bow (`concepts/marbre/assets/js/marbre.js`), rescaled to a violin bow (≈745 mm, 11.8 mm frog, 9.5 mm hair) | — | Stick with camber and octagonal section near the frog, hatchet head and tip plate, ebony frog with silver and mother-of-pearl, a hair ribbon that bends where it presses on the plate's edge. |
| `js/live.js` | written for this concept (round 6) | — | The conductor: the bow's own strokes (2–4 s, up-bow and down-bow in turn, a 0.45–0.9 s rest after each), the plate's amplitude step by step (catch, stick-slip, ring-down), and the re-tune when the ensemble changes (the stroke stops within 0.18 s, the bow lifts and slides along the edge to the new bowing point in 0.72 s). Seeded integer hashes and plain arithmetic, counted in whole steps (rule 40). |
| `js/page.js` | written for this concept | — | The live loop (50 simulation steps a second on the clock, at most 60 frames a second, pause off screen and in a hidden tab, frame-rate downshift), scroll and the four buttons choosing the figure, reduced motion, deterministic hooks. Round 6 removed "drag the plate to bow": the bow plays by itself. |

## Textures

| File | Source | Author | Licence | What was done |
|---|---|---|---|---|
| `tex/plate-wear-2k.webp` | ambientCG **Scratches004** (https://ambientcg.com/view?id=Scratches004), **Fingerprints001** (https://ambientcg.com/view?id=Fingerprints001), **Smear004** (https://ambientcg.com/view?id=Smear004), 2K-JPG sets | ambientCG (Lennart Demes) | CC0 1.0 | Packed into one plate-sized map: R = Scratches004 opacity (×1.15), G = Fingerprints001 opacity masked to six soft patches near the edges where hands set a plate down, B = Smear004 roughness (wipe marks, reduced). 2048², Lanczos, WebP q82. In the shader: fingerprints and wipes raise the roughness, scratches lower it and brighten the metal. |

Generated at runtime (code, no files): the brushed finish (grooves a tenth of a millimetre apart, each with its own depth
along its length, a few longer scratches), the studio environment (an equirectangular map built from the same numbers as
the lights: a 0.6 × 0.45 rad softbox camera-left, a narrow hard strip behind, a pale card behind that the metal mirrors as
a gradient; then PMREM), the sand's grain-scale texture, the horsehair, the pernambuco grain, the silver winding.
The print grain is an inline SVG `feTurbulence` in CSS, laid once over both the still and the live canvas.

## Fonts (concept only)

| File | Family | Licence | Production face to license |
|---|---|---|---|
| `fonts/geist-latin.woff2`, `fonts/geist-latin-ext.woff2` | Geist (Google Fonts v5; variable weight 100–900), Vercel / Basement Studio | SIL OFL 1.1 (`fonts/OFL-Geist.txt`) | Neue Haas Grotesk Display and Text (Commercial Type) |
| `fonts/libre-caslon-text-italic-latin.woff2` | Libre Caslon Text Italic (Google Fonts v5), Pablo Impallari, Rodrigo Fuenzalida | SIL OFL 1.1 (`fonts/OFL-LibreCaslonText.txt`) | Adobe Caslon Pro Italic (Adobe) |

## The stills (first frame = fallback)

All rendered headless from this scene (Playwright Chromium, SwiftShader, 4× MSAA as live), resized with Lanczos 3 and
encoded with sharp (AVIF q52, WebP q80, mozjpeg q82). The tall frames use the phone's grain subset, so the phone's still
and its live first frame match.

| Files | What | Used for |
|---|---|---|
| `img/resonance-hero-wide-{2560,1280}.{avif,webp,jpg}` | the first frame (the full-orchestra figure, the bow touching the far edge), desktop reference frame 16:10 | first paint, no-WebGL, reduced motion |
| `img/resonance-hero-tall-{1170,780}.{avif,webp,jpg}` | the same, phone reference frame 390:844 | first paint on phones |
| `img/fig-{trio,quartet,chamber,full}-{1600,800}.{jpg,webp}` | the four settled figures | reduced motion and no-WebGL: the four stills |
| `img/detail-{880,440}.{avif,webp,jpg}` | a 4:5 crop at 1:1 from the 2560 first-frame still (the knob and the lace) | the print in "About" |

## Byte budget (file sizes as served, 10 Oct 2026)

| | bytes |
|---|---|
| First view, desktop (page, the Geist font, the 2560 AVIF still, three.js, the five scene modules, the wear texture), 11 Oct with `live.js` | 1.54 MB (0.85 MB with gzip on the HTML and JS) |
| Everything published (excluding `tools/` and `shots/`, which are working material) | 3.26 MB |

Budget: first view ≤ 4 MB, everything ≤ 12 MB. The figure stills and the detail print load lazily.

## References (not published)

`tools/refs/` holds photographs used only to check the physics and the look; they are not part of the page and must not be
published: Wikimedia Commons — "Chladni pattern 1", "Chladni pattern 2" (Elmar Bergeler, CC BY-SA 3.0), "Chladni III",
"Chladni I" (Carlos Andrés Reyes, CC BY 2.0), "Chladni Plate Pattern 2" (Revisorius, CC BY-SA 4.0), "Bowing chladni
plate" (public domain engraving), the 1787 title pages of *Entdeckungen über die Theorie des Klanges* (public domain).
`tools/` as a whole (scripts, renders, references) is working material, not part of the site.
