/ proof-of-concept / steelsim41
Sign in
proof-of-concept / steelsim41
steelsim41 / README.md
8.7 KBPreviewCodeBlameHistoryRaw

steelsim41#

A browser application for designing a virtual cymbal and simulating a drumstick strike on it. The cymbal is described by a short JavaScript script (material, bounding box, outline, thickness field, stress-free profile, mount, and losses) that the user edits in the page, or that an AI writes for them. The sound is computed by solving the geometrically nonlinear (von Karman) plate equations with in-plane inertia and a spatially varying thickness, stepped explicitly on the GPU through WebGPU. The model and the numerical method are described in THEORY.md.

The physics and most of the interface come from steelsim31 (the hanging steel sheet); the changes here are the scripted design, the thickness field, the mount, an arbitrary outline (including a center hole), and the removal of the push excitation, the hit series, and the feature-list shape editor.

The app is deployed by the deploy workflow on every push to main: https://steelsim41--proof-of-concept.vault1-sites.magland.org/

Running#

Requires Node 20 or later and a browser with WebGPU (Chrome or Edge 113+; Firefox and Safari with WebGPU enabled).

npm install
npm run dev        # development server
npm run build      # static site in dist/
npm test           # all tests (see below)

Using the app#

The sidebar has three parts: the design script, the strike and simulation parameters, and the controls. The main area shows the plate (2D color map or a lit 3D height field; drag to orbit, wheel to zoom), the movie and playback toolbar, and the spectrogram of the listener signal.

Design script. Pick a preset from the dropdown (a flat steel rectangle by default; a bowed rectangle, a flat disc on a stand, a 16 inch crash, a 20 inch ride, an ellipse with a twist, an 18 inch china) and edit it freely; the script is compiled as you type (or with Ctrl+Enter) and the view shows the resulting stress-free shape, colored by height or by thickness. Errors are reported under the script, and a summary lists the bounding box, the material, the thickness range, the mass, the grid, the profile's peak-to-peak height and largest slope (with a warning above the shallow-shell range), the mount, and the losses. Revert restores the selected preset.

The script is a plain JavaScript function body that returns the design object. All lengths are meters; inch and mm are conversion constants. Every field function receives (x, y, r, theta): the point relative to the center of the bounding box, its distance from the center, and its angle from the x axis.

const D = 16 * inch, R = D / 2;
return {
  name: "16 inch crash",
  material: { E: 110e9, nu: 0.34, rho: 8800 },              // Pa, -, kg/m^3
  bounds: { width: D, height: D },                           // bounding box [m]
  inside: circle(D, 12 * mm),                                // outline, or (x, y, r, theta) => boolean; omit for the whole box
  thickness: (x, y, r) => lerp(1.4 * mm, 0.8 * mm, clamp((r - 60 * mm) / (R - 60 * mm), 0, 1)),
  profile: (x, y, r) => 16 * mm * (1 - (r / R) ** 2) + dome(r, 60 * mm, 12 * mm), // stress-free height [m]
  mount: { radius: 20 * mm, stiffness: 2e4, damping: 5 },   // or null for a free plate
  loss: { sigma0: 0.6, sigma1: 2.5e-4, sigmaIn: 20 },
};

thickness and profile may be numbers or functions; inside, mount, and loss are optional (mount may also give x and y). Helpers in scope: circle(diameter, hole), ellipse(width, height, hole), rectangle(width, height, hole) (outlines), clamp, lerp, smoothstep(e0, e1, x), bump(d, radius, height) (Gaussian), dome(d, radius, height) (cosine dome with zero slope at the rim), bow(s, span, sag, offset) (parabolic bow), and noise2d(seed, length) (a smooth random field of unit RMS). The profile's mean plane is removed before use, since only its slopes enter the physics and a tilt would add a spurious membrane coupling.

Strike and simulation. The strike point is given as fractions of the bounding box (the yellow ring in the view), with the stick speed, its effective mass, the Hertz contact stiffness and exponent, and the tip radius. The listener distance and direction can be changed while a simulation runs. The grid spacing (8 mm by default), the in-plane mass factor, the sample rate, and the frame rate take effect at the next Start; the panel reports the grid, the frequencies at which grid dispersion lowers the partials by 3 and 10 percent, the time step, and the 60 dB decay times of the design's losses. The rigid-body projection applies to free plates only; a mounted plate takes the reaction of the strike through the mount.

Running. Press Start. The stick strikes the plate at rest in its stress-free shape, and the recording of audio and movie frames starts at the impact. The view shows the total shape by default (profile plus vibration); untick Show profile to see the vibration alone, which makes a small vibration on a curved cymbal visible. Pause halts the run temporarily; Stop ends it, keeps the audio and movie, and unlocks the script and the fields. To extend a finished run, raise the duration and press Continue. After the run, drag the time slider to explore the movie; Play starts the audio from the slider position and moves the movie and the spectrogram cursor with it; Live returns to the running view and Design to the stress-free shape. The audio can be played or downloaded as a 16-bit WAV at any time; Absolute level maps 1 Pa to full scale shifted by the gain slider (the peak level in dBFS is shown with a clipping warning), and Loudness and Peak normalize each run instead.

URL parameters. ?preset=crash-16 selects a preset; any strike, listener, or numerics parameter can be preset in SI units, for example ?preset=ride-20&spacing=0.006&duration=3&autostart=1.

The default grid spacing of 8 mm keeps the simulation interactive but lowers the bending partials by a few percent near 1 kHz and by 10 percent a few kilohertz higher, depending on the thickness; the panel reports the two frequencies. The stable time step is set by the in-plane wave speed (about 1.3 microseconds at 8 mm for steel and 1.7 for bronze) and does not depend on the thickness, so a thick ride costs the same per step as a thin crash; a 16 inch cymbal at 8 mm has about 2000 nodes and runs at 60 to 70 thousand steps per second on an integrated GPU, or roughly 50 s for 4 s of sound.

Tests#

npm test runs three groups of tests with Node's built-in runner.

Layout#

↑↓ move↵openescclose