Each wave has its own character. Hover to see it up close. Every shape is periodic - it repeats, but each one repeats differently.
Pick a wave, drag the sliders, see the code update live.
One flag. The library picks a random wave, holds it, then smoothly morphs into the next one. Different every time you load the page.
A ring sampled with shift normally tears its seam open the moment it lands on a new wave - every formula has its own period. The 'closing' pool fixes that: all 19 waves in it share one base period, so a sweep of ring.period times the lobe count closes through every morph. The loop keeps changing shape and never shows a seam.
// These four are plain sketch constants. You pick them.
const LOBES = 6; // how many lobes around the ring
const SEGS = 3000; // points drawn around the ring
const RADIUS = 200; // base radius in pixels
const WOBBLE = 70; // wave height in pixels
// The sampler auto-shifts through the 'closing' pool.
let ring = Waves.createSampler({
shift: true,
group: 'closing',
amplitude: 1
});
// ring.period comes straight from the library: 62.8319
// for the closing pool. That is why the sweep keeps
// closing even as the wave shifts. You never type it.
let sweep = ring.period * LOBES;
for (let i = 0; i < SEGS; i++) {
let frac = i / SEGS;
let r = RADIUS + ring.sample(frac * sweep, t) * WOBBLE;
vertex(cos(frac * TWO_PI) * r, sin(frac * TWO_PI) * r);
}No angles, no steps - wave output IS the velocity. Fifteen trails in five colors, grouped into a cold pair and a warm pair of shift-samplers. Same-color walkers share a sampler, so they move as a family; the two temperature families transform on different schedules. Rendered on an 8px grid for that old-school bitmap feel.
// Cold pair drives blue + cyan walkers.
// Warm pair drives red + yellow + pink.
let coldX = Waves.createSampler({
shift: true, group: 'gentle',
amplitude: 5, frequency: 0.4, seed: 0
});
let coldY = Waves.createSampler({
shift: true, group: 'gentle',
amplitude: 5, frequency: 0.3, seed: 77
});
let warmX = Waves.createSampler({
shift: true, group: 'gentle',
amplitude: 5, frequency: 0.4, seed: 311
});
let warmY = Waves.createSampler({
shift: true, group: 'gentle',
amplitude: 5, frequency: 0.3, seed: 488
});
// Wave output IS velocity:
let xS = isCold ? coldX : warmX;
let yS = isCold ? coldY : warmY;
wx += xS.sample(t * 1.8 + phase, t);
wy += yS.sample(t * 2.1 + phase, t);Two independent wave samplers drive X and Z height.
Every cell is a unique sketch powered by the library. Waveforms as engines for motion, color, shape, and pattern.
Add the script tag. Call the function. Done.
<script src="p5.waves.js"></script>
function draw() {
for (let x = 0; x < width; x += 3) {
let y = Waves.wave(x, {
wave: 'mountain peaks',
t: millis() / 1000,
amplitude: 80
});
point(x, height / 2 + y);
}
}