80c0b6dPlate dynamics lab: plates in realizable mounts under gravity, explicit finite differences on WebGPUJeremy Magland 1# Plate Dynamics Lab
7540997Save the run as a movie: the scene, its caption and the soundJeremy Magland 3An interactive lab for the dynamics of a thin metal plate held the way a plate can actually be held on a bench: hung from a thread or two, resting on foam or felt pads, on a cymbal stand, gripped in a vise, or pinched between finger and thumb. Gravity is part of the model. A hung plate swings on its thread and rocks about its hole while it rings; a plate on pads sags into them and can be knocked off them; a cantilever droops and then vibrates about its droop. You choose the plate (a rectangle or an ellipse), the material, the mount and its orientation, and strike the plate or pull it aside and let go; the app shows the plate moving in its mount, the flexural displacement and surface stress fields, the rigid-body swing and the support forces over time, a probe time series and spectrum, and the radiated sound, which you can play or save. The scene can also be saved as a movie, at whatever orientation and colouring you have set, with that sound as its audio track.
80c0b6dPlate dynamics lab: plates in realizable mounts under gravity, explicit finite differences on WebGPUJeremy Magland 4
5The numerics run in [numbl](https://github.com/flatironinstitute/numbl), a MATLAB-syntax environment executing in the browser. The MATLAB source shown on screen is the source that ran: edit any file and rerun. The time loop, which is hundreds of thousands of steps for a run of a few seconds, has a WebGPU port that is on by default; the setup, the statics, the stresses, the sound and the spectra run in numbl in double precision, and the double-precision loop in numbl remains the reference and the fallback. No physics is computed in JavaScript outside that port; the host draws arrays and plays samples.
7This lab succeeds [plate-mechanics-sim](../plate-mechanics-sim), keeping its shapes, materials, source view and result panels, and replacing its idealized edge conditions with mounts, its several methods with one explicit scheme, and its millisecond windows with seconds.
9## Layout
11```
12packages/core problem spec, .m generation, numbl runner, WebGPU time loop, result typing, field primitives (no UI)
13packages/core/src/m the MATLAB library: plate_fd.m, plate_model.m, plate_mount.m, plate_prestress.m, ...
14packages/components React components: code view, parameter panel, 2-D field view, 3-D scene, plots, equations, run status, audio
15apps/lab the app: one arrangement of the components
16```
18## The model
20The plate is a damped Kirchhoff-Love plate in its own coordinates, with two extra terms that carry gravity:
22 mu w_tt + 2 mu sigma0 w_t - 2 mu sigma1 lap(w_t) + D lap(lap(w)) - div(N grad w) = f + mu g_n + f_mount
24The normal component of gravity, `mu g_n`, loads the plate transversely and gives a horizontal plate its static sag. The in-plane component puts the plate under its own weight, and `N` is the resulting in-plane stress resultant, found once at setup by a plane-stress solve with the mount's constraints (the hole a thread passes through, the jaws of a vise, the pinched patch). The membrane term `div(N grad w)` is how gravity acts on a hanging plate's motion: a tilted element under tension is pulled back. For a rigid rotation of the plate about its hole the term integrates to the compound-pendulum stiffness `m g d` exactly, and the lab assembles it with the same difference operators as the plane-stress energy, so this identity holds to rounding rather than to truncation error (`packages/core/test/fd.test.ts` checks it). The swing of a hung plate and the small stiffening of its lowest flexural modes therefore come from one operator, and a plate hung from below its centre of mass is correctly unstable.
26Each mount is data on the grid (`plate_mount.m`):
28- **One thread.** A plate hung from one point turns in its plane until its centre of mass is straight below the hole, which fixes the direction of gravity. For small out-of-plane motion the thread is a transverse spring of stiffness `T/L` at the hole (the linearized pendulum), plus a torsional spring `G J / L` about the vertical, which for any real thread is so weak that the spin has a period of minutes.
29- **Two threads** of one length from hooks at one height: the line of the holes hangs horizontal, statics shares the weight, and the lab refuses a geometry in which a thread would go slack.
30- **Pads.** Rectangular patches of a stiffness and damping per unit area, one-sided: a pad pushes while compressed and lets go when the plate lifts off. Which pads carry the plate at rest is found by an active-set iteration.
31- **Cymbal stand.** A felt washer below and one above the centre, each preloaded by the wing nut; the rod, the hole and the cymbal's bow are not modelled.
32- **Vise.** The nodes within the jaw depth are held at zero; the clamped condition follows from the bending energy (a second difference centred on a held node penalizes the slope at the clamp). Any tilt from hanging down (0°) through horizontal (90°) to standing up (180°, self-weight in compression).
33- **Fingers.** A two-sided spring and dashpot over a patch, also fixed in the plane; hanging (0°) to flat (90°).
35Every quantity of the model has an equation in the app's Equations tab, and every idealization a line in its validity notes.
37## The scheme
39The spatial operators are the sparse energy-method matrices of `plate_bending.m` (the 13-point biharmonic stencil inside, the free-edge conditions as natural conditions on the edges, held nodes dropped) and `plate_prestress.m`. The time stepping is the second-order explicit scheme of the old lab, with two changes.
41First, the state is the departure from the static equilibrium, split into three rigid-body coordinates (a translation along the normal at the mount's pivot, and the rotations about the horizontal and the vertical in-plane axes through it) and a flexural remainder that is mass-orthogonal to them (`plate_rigid_basis.m`). The rigid coordinates obey three scalar equations with the exact reduced stiffness `R' A R` (the pendulum, the thread springs, the thread's torsion) and are coupled to the flexural field through `A R`; the flexural field obeys the grid equation with the rigid acceleration projected out. The split is exact, and it is what keeps a millimetre swing and a micrometre vibration apart in single precision: bending annihilates the rigid basis, so the bending operator is never applied to the swing.
43Second, the stable step is the von Neumann bound of the bending scheme extended by a Gershgorin bound on the extra stiffness of the prestress, the springs and the engaged contacts (`plate_model.m`), and the dashpots, treated explicitly, keep the step below half of their own limit. A run of a few seconds on the default 41 x 27 grid is about 650,000 steps.
45Contacts are evaluated at the current step, one-sided, with the static part subtracted so that the forces are linear in the departure until a contact opens. A release starts from the static deflection under a force at the pull point, scaled to the asked displacement and split into rigid and flexural parts.
47## The GPU loop
49`packages/core/src/gpu/fdLoop.ts` is `plate_fd_loop.m` as compute shaders: five dispatches per step (strains, forces, the rigid-body update with the reductions it needs, the field update, and the audio and blow-up reductions). The setup (`plate_fd_setup.m`) and the output (`plate_fd_output.m`) run in numbl, unchanged and in double precision; the worker hands the setup struct to the GPU and the loop's record back. Editing `plate_fd_loop.m` sends the loop back to numbl, where the edit is live, and `packages/core/test/gpu.test.ts` compares the port with the numbl loop for every mount.
51The port applies the operators in their factored form, a difference stencil, then weights, then the transposed stencil, rather than as the assembled sparse matrix. In single precision the assembled 13-point stencil loses the smooth modes: the biharmonic of a smooth field is a small residual of large cancelling terms, and the rounding of the field alone is a few percent of it at low wavenumbers, which is where a long run lives. In factored form each stencil is exact integers applied to nearby values, the scales sit in the weights where rounding is harmless, and the port agrees with the double-precision loop to a relative L2 error of about 1e-6 in the fields, the rigid coordinates and the support forces over the test windows.
53Cost. On an integrated Intel GPU the default run (a 300 x 200 mm sheet on a 41 x 27 grid for 3 s, about 700,000 steps) takes between 15 and 50 s in the loop, the spread being the GPU's own load, plus about 1.5 s in numbl for the setup and the output; a 61 x 41 grid for 1 s takes about a minute. The same loop in numbl runs at roughly 0.8 us per cell-step, so the default run would take some ten minutes there; the estimate shown before a run is calibrated by the runs before it.
55## Scenarios
57The scenario picker sets everything at once; every field stays editable afterwards.
59- Steel sheet on a thread (the default): sways at about 0.8 Hz, rocks about the hole at about 2.9 Hz, spins slowly, and rings.
60- Gong on two cords: a bronze disk, struck at the centre.
61- Glockenspiel bar on nodal pads: felt strips under the nodes of the first mode.
62- Sheet on four foam blocks: a hard strike lifts it off the pads.
63- Cymbal on a stand: preloaded felts at the centre of a flat bronze disk.
64- Cantilever in a vise: a steel strip pulled down 10 mm and released.
65- Disk pinched in the hand: a soft, lossy grip near the rim.
7540997Save the run as a movie: the scene, its caption and the soundJeremy Magland 67## Saving a run
69Two exports, both written by the browser itself with no server and no bundled encoder.
71- **The sound**, as a 16-bit mono WAV at the simulation's own sample rate, peak-normalized (the Rayleigh estimate is not a calibrated level, so the absolute scale would be misleading either way).
72- **The movie**, as WebM (VP9 and Opus) or MP4 where the browser records it, 960 pixels wide: the 3-D scene as it stands, at its present orientation, exaggeration and colouring, with a title bar carrying the simulated time and a caption bar carrying the field, its colour scale and the exaggeration factors, so the file explains itself. The sound is the audio track. `packages/components/src/video/recordMovie.ts` composites the scene and the caption onto a canvas, captures that canvas as a video track, plays the sound into an audio track, and lets `MediaRecorder` mux the two.
74The recording is real time, which is what using nothing but the platform costs: a three-second movie takes three seconds to write, the tab has to stay in front, and a machine that cannot keep up drops frames rather than slowing down (the lab says how many of the run's frames it drew). Below real time the picture and the sound are slowed together, so the sound drops in pitch as a tape would. A run whose frames would need more than about sixty a second cannot have all of them drawn at real time, and the lab says so and offers the slower speeds.
78The lab is deployed at <https://plate-dynamics-lab.vault1-sites.magland.org/>. `.mochi/workflows/deploy.yml` typechecks, runs the tests and builds `apps/lab` on every push to `main`, then publishes it; a regression in the tests stops the deployment. The site sits at the root of an origin of its own, which the repository's site label gives it, so the build's base path comes from `configure-pages` rather than being written into the Vite config. Having its own origin also lets the page use `localStorage`, which is where the lab keeps your settings and your source edits: a site served under the forge's own host is sandboxed, and reading storage there throws.
80c0b6dPlate dynamics lab: plates in realizable mounts under gravity, explicit finite differences on WebGPUJeremy Magland 80## Running
82```bash
83npm install
84npm run dev # the lab, http://localhost:5173
85npm test # vitest: the .m files in numbl under Node, plus the GPU loop against the numbl loop when a WebGPU device is available
86npm run typecheck
87npm run build
88```
90`packages/core/test/gpu.test.ts` runs the WebGPU loop from Node against desktop WebGPU through the optional `webgpu` package (prebuilt Google Dawn); `npm i --no-save webgpu` installs it. Without the package or a GPU adapter the suite skips itself.
92The `.m` files also run unchanged from the numbl CLI, Octave or MATLAB:
94```bash
95npx numbl run --path packages/core/src/m my_script.m
96```
98## Honest limits
100- Kirchhoff-Love thin-plate theory: no shear deformation or rotary inertia, so thick plates and very high modes are wrong.
101- Small-angle theory throughout. The threads are linear springs, the pendulum motion is linearized, and the plate's in-plane motion (a swing in its own plane, a rotation about the normal) is not part of the model. A pull beyond a few degrees of swing leaves this regime; the spin about a thread, which has no gravitational stiffness, is exact at any angle and is drawn as a true rotation.
102- Mounts are idealizations with order-of-magnitude constants: a thread has one tension, a pad is a spring and dashpot per unit area, a vise is perfectly rigid, a hand is a guess.
103- Free edges and curved edges are first order at the boundary; the stress at a hole or a jaw is that of a point or line constraint on the grid and depends on the spacing.
104- Damping is the two-parameter loss model plus the mount's dashpots, chosen, not measured; the sigma1 loss acts on the flexural part only.
105- The probe series are averages over each output sample; stress fields are evaluated only at the saved frames.
106- Radiated sound is an estimate of the waveform, not a calibrated level: Rayleigh's integral in a baffle, or a rough front-back cancellation for an unbaffled plate.
7540997Save the run as a movie: the scene, its caption and the soundJeremy Magland 107- The movie is recorded in real time from the browser's own encoder, so its length is the length of the run divided by the speed, its bitrate is the browser's choice, and frames are dropped when the machine falls behind. It is a recording of the animation on screen, not a re-render: the time resolution is the run's saved frames, not its time step.
80c0b6dPlate dynamics lab: plates in realizable mounts under gravity, explicit finite differences on WebGPUJeremy Magland 108- The GPU loop is single precision. The rigid-flexural split and the factored operators keep it close to the double-precision loop, but a run of many seconds accumulates rounding that the tests over tens of milliseconds do not measure.