00
classic sine
sin(x*.1)*.4
gentleclosingT 62.83
Each wave is a deterministic function: pass a number in, get a number back. The output range is roughly [−0.5, +0.5] before you scale it. Every preview here is traced live in SVG - no <canvas> - and the numbers are bit-identical to p5.waves v3.6.0.
The reference shapes. Smooth, periodic, easy to predict.
Modulated sines that emphasise crests or troughs.
Quantised waves: ceil / round / modulo carve smooth shapes into plateaus.
Two or more sines multiplied, added, or modulated.
No trig, just modulo and absolute value. Sharp, predictable.
Tangents, noise, logarithms and random values. Most are flagged harsh: they use a seeded PRNG or a non-trig function with surprising spikes.
A wave is periodic when there is a distance T such that wave(x) == wave(x + T) for every x. That distance is the wave's period. Knowing it lets you close a shape cleanly: sample a wave around a ring over an integer number of periods and the last point lands exactly on the first - no seam.
Period coordinates. wave(x) treats x as a generic coordinate - the same mental model as noise(x) - not an angle. Nine of the 35 waves are not sine-based at all (square, triangle, ramp, noise…), so radians would be meaningless for them.
Every period value was measured, not derived: the periodicity harness in the p5.waves repo samples each wave, finds the smallest T where the mean error between wave(x) and wave(x + T) stays below 0.002, then confirms with multiples. vanilla.waves produces numerically identical output, so the same periods hold.
Build an SVG path from the sampled values. Set an integer lobes count and read the sweep from sampler.period. Use the closing group when shifting; its periods are measured approximations (see the guide).
// A closed wobble ring in SVG. No visible seam. const ring = VanillaWaves.createSampler({ group: 'closing', shift: true, amplitude: 30 }); const lobes = 8, steps = 240; const sweep = ring.period * lobes; // integer multiple = clean close let d = ''; for (let i = 0; i < steps; i++) { const a = (i / steps) * Math.PI * 2; const r = 140 + ring.sample(i / steps * sweep, t); d += (i ? 'L' : 'M') + (200 + r*Math.cos(a)) + ' ' + (200 + r*Math.sin(a)); } path.setAttribute('d', d + 'Z'); // Z closes it
26 waves close cleanly, 9 don’t. Each card above also shows its measured period.
| Period T | Waves |
|---|---|
| 2.03 | 33 smooth solid sine |
| 16.67 | 20 saw down, 21 saw up |
| 17.75 | 25 fuzzy pulse (valid close, not the fundamental) |
| 31.42 | 1 sine, 2 sharp peaks, 8 zig-zag sine, 11 steps down, 13 squared sine, 26 up down pulse |
| 33.33 | 18 triangle |
| 40 | 3 square, 4 pulse |
| 50 | 19 ramp |
| 62.83 | 0 classic sine, 5 stepped sine, 6 mountain peaks, 7 valleys, 9 batman, 10 offset sine, 12 steps, 14 bumpy sine, 15 wobble sine, 22 shake out, 31 round linked sine, 34 spike sine |
| — | no clean close: 16 up down noise, 17 meta sine, 23 grow random, 24 noise, 27 bald patch, 28 fuzzy peak sine, 29 ramp up sine, 30 triangle sine, 32 half sine |
For a shape that stays closed while shifting between formulas, use group: 'closing' - a pool of 19 waves that all share the base period 62.8319 - and read the sweep from sampler.period. See Closing shapes in the guide.