Coupled Meshes + String — User Guide

Vectorised mass‑spring physical model. Two 8×8 meshes (140 nodes) connected by a 12‑node string, with 235 linear springs. Uses matrix‑vector products for force scattering and Verlet integration – the hot loop stays in Praat’s compiled kernels, yielding 5‑20× speedup over scalar implementations. Five presets: Bright Metallic Plate, Dark Gong, Prepared Piano, Percussive Short, Sustained Drone.

Author: Shai Cohen Affiliation: Department of Music, Bar‑Ilan University, Israel Version: 1.1 (2026) License: MIT License Repo: GitHub
Contents:

What this does

Coupled Meshes + String is a physical modelling synthesiser that simulates two 8×8 mass‑spring meshes connected by a 12‑node string. The system has 140 nodes and 235 linear springs, with independent stiffness and damping per spring group. The simulation uses Verlet integration and is fully vectorised – force scattering and state updates are expressed as matrix‑vector products (mul#) and element‑wise vector arithmetic, keeping the hot loop in Praat’s compiled kernels rather than the script interpreter.

Architecture:
  • Mesh 1 (nodes 1–64) – 8×8 grid, 112 springs (horizontal + vertical).
  • String (nodes 65–76) – 12 nodes, 11 springs.
  • Mesh 2 (nodes 77–140) – 8×8 grid, 112 springs.
  • Coupling springs – attach each mesh to a string endpoint (nodes 65 and 76) via bilinear interpolation.
  • Excitation – half‑sine pulse applied to the string at a user‑defined position.

Output is taken from node 70 (centre of the string). The audio is downsampled from the model rate to the sample rate, with a cosine fade‑out applied at the end.

Quick start

  1. In Praat, run the script CoupledMeshString.praat (no sound selection needed – the script generates its own audio).
  2. Choose a Preset:
    • Bright Metallic Plate, Dark Gong, Prepared Piano, Percussive Short, Sustained Drone
  3. For custom mode (preset = Custom), adjust parameters as desired:
    • StrStiffness, M1Stiffness, M2Stiffness – spring constants.
    • StrDamping, M1Damping, M2Damping – per‑spring damping.
    • CouplingK, CouplingZ – coupling spring stiffness/damping.
    • GlobalZ0 – global damping (Verlet friction).
    • ExcitationPosition – where on the string the impulse is applied (0–1).
    • ExcitAmplitude – strength of the initial impulse.
  4. Set Duration, SampleRate, and ModelRate (the physics update rate).
  5. Click OK. The script runs the simulation (vectorised, fast) and creates a Sound object named Modeled_Instrument.
Tip: For a bright, metallic sound, try Bright Metallic Plate. For a long, evolving drone, use Sustained Drone. The model rate can be lowered for faster simulation (e.g., 1102 Hz) – the audio is linearly interpolated to the sample rate.
Important: This simulation is computationally intensive. The vectorised implementation is 5–20× faster than scalar loops, but for long durations (e.g., 10 s) it may still take 10–30 seconds. Reduce ModelRate to speed up. Keep stability in mind: str_k < 0.5, mesh_k < 0.25 when downsampling.

The 5 presets (+ Custom)

PresetStr kM1 kM2 kStr zM1 zM2 zCoupling kGlobal z0Description Bright Metallic Plate0.490.240.240.00020.00050.00050.120.00003。 Dark Gong0.420.240.200.00010.00040.00100.150.00008。 Prepared Piano0.490.220.180.00030.00080.00080.250.00005。 Percussive Short0.350.200.200.00250.00300.00300.080.0002。 Sustained Drone0.480.230.230.000050.000150.000150.100.00002。

Physics model

Verlet integration (vectorised):
new_u = u + (u - u_prev)·(1 - z0) + frc
where frc includes spring forces, coupling forces, and excitation.

Spring force (per spring):
dist = u[i1] - u[i2]
vel = (u[i1] - u_prev[i1]) - (u[i2] - u_prev[i2])
fspr = k·dist + z·vel

Coupling (bilinear interpolation):
Each mesh is attached to a string endpoint via bilinear interpolation of the 2×2 neighbourhood around the attach point.
pos_mesh = Σ w_ij·u[node_ij]
fc = k_c·(pos_mesh - pos_string) + z_c·(vel_mesh - vel_string)
The force is then distributed back to the four mesh nodes and the string endpoint.

Boundary conditions: All mesh boundary nodes (edges) are fixed at zero displacement (masked out in the Verlet step). The string endpoints (nodes 65 and 76) are free to move – they are coupled to the meshes.

Excitation: A half‑sine pulse of length 5 ms (at model rate) is applied to two adjacent string nodes, weighted by the fractional excitation position. The amplitude is user‑controlled.

Output: The displacement of node 70 (centre of the string) is recorded and linearly interpolated to the sample rate. A cosine fade‑out (30 % of duration, max 400 ms) is applied to avoid clicks.

Vectorised implementation (speed optimisation)

⚡ How vectorisation works

Instead of iterating over each spring in a Praat script loop (slow), the simulation precomputes two matrices:

  • diff##[s, n] – +1 if spring s starts at node n, -1 if it ends at node n. Multiplying diff## by the displacement vector u# gives the per‑spring extension dist# in one matrix‑vector product.
  • scatter##[n, s] – -1 if spring s starts at node n, +1 if it ends at node n. Multiplying scatter## by the per‑spring force vector fspr# scatters the forces back to the nodes.

The hot loop then consists of three mul# calls, element‑wise vector arithmetic, and a masked Verlet update – all operations that Praat executes in compiled C code, not in the script interpreter. Typical speedup vs. a scalar loop: 5–20×.

The coupling and excitation blocks are small scalar operations (bilinear interpolation, force distribution) and are not vectorised – they represent a negligible fraction of the runtime.

Parameters & defaults

Simulation

ParameterRangeDefaultDescription
Duration0.5–302.0
SampleRate8000–9600011025
ModelRate500–200002205

Resonator (spring constants)

ParameterRangeDefaultDescription StrStiffness0.1–0.60.49。 M1Stiffness / M2Stiffness0.05–0.40.24。 StrDamping / M1Damping / M2Damping0.00005–0.010.0003 / 0.0008 / 0.0003。 CouplingK0.01–0.50.10。 CouplingZ0.00001–0.0010.0001。 GlobalZ00.00001–0.0010.00005。

Excitation / geometry

ParameterRangeDefaultDescription ExcitationPosition0–10.3。 ExcitAmplitude0–21.0。 M1AttachX / M1AttachY0–10.5 / 0.5。 M2AttachX / M2AttachY0–10.5 / 0.5。

Output

ParameterDefaultDescription Draw_visualizationyes。 Play_resultyes。

Visualization (Praat picture)

When Draw_visualization = 1, the script draws a 4‑panel figure:

Tip: The spectrogram reveals the modal behaviour of the coupled system. The string’s fundamental and harmonics are visible as horizontal bands; the meshes contribute higher, more diffuse energy.

FAQ / troubleshooting

Simulation is slow / takes too long

The number of physics steps = duration × model_rate. For a 10‑second sound at 2205 Hz model rate, that’s 22,050 steps. Each step includes 235 spring force calculations (via matrix‑vector products) plus coupling and excitation. The vectorised implementation is already optimised – if it’s still too slow, reduce ModelRate (e.g., to 1102 Hz) or Duration.

Output sounds unstable / explodes

Check that the stiffness values are not too high. For stability at the given model rate, keep str_k < 0.5 and mesh_k < 0.25. If you lower the model rate, you may need to reduce stiffness proportionally. Also ensure GlobalZ0 is not too low – a value of 0.00005–0.0002 is safe.

No sound / very quiet

The output is taken from node 70 (centre of the string). If the string is heavily damped or the coupling is weak, the amplitude may be very low. Increase ExcitAmplitude (e.g., to 2.0) or reduce damping. Also check that CouplingK is not zero – without coupling, the meshes do not affect the string.

Why two meshes?

The two meshes act as resonators – they absorb energy from the string and re‑radiate it, creating a complex, multi‑modal decay. The attachment points determine which modes of each mesh are excited. This simulates a physical instrument where the string is coupled to a soundboard (or two soundboards).

Linear interpolation vs. sample‑rate conversion

Audio output is produced by linearly interpolating the model‑rate displacement values to the target sample rate. This is fast and avoids artefacts from resampling. The model rate should be at least 2× the highest frequency of interest – for most presets, 2205 Hz is sufficient.

Cosine fade‑out

The fade‑out is a half‑cosine (equal‑power‑ish) over 30 % of the duration (capped at 400 ms). This provides a smooth, click‑free ending without an abrupt cutoff. The fade length is printed in the summary panel.