You give it a number. It gives you a number back.
That's the whole idea. The rest is just picking which wave shape you want.
Two script tags. p5.js first, then p5.waves. Done.
No npm. No build step. Just script tags.
Copy this into a new p5 sketch. You'll see a moving wave line.
That's the whole API in action. Waves.wave() takes a position, returns a number. Use that number for y-position, size, color, rotation, opacity — anything that takes a number.
Now change 'mountain peaks' to 'batman'. Or 'wobble sine'. Or 'fuzzy pulse'. Each one has its own personality. There are 34 to explore.
From lazy to precise — all three return the same kind of number.
Start lazy. Add options when you need them.
Anything. Seriously. Here are some ideas:
If it accepts a number, a wave can drive it.
You don't need all of these. Start with the top three. Add more when you're curious.
| Option | What it does | Default |
|---|---|---|
wave | Which shape. Pick a name like 'sine', a number like 9, or blend two: ['sine', 'triangle']. | random |
t | Time. Makes it move. Pass millis() / 1000. No time = frozen wave. | 0 |
amplitude | How big. Output swings from -amplitude to +amplitude. | 100 |
| Option | What it does | Default |
|---|---|---|
range | Map output to any range you want. [0, 255] = perfect for color values. Replaces amplitude. | null |
frequency | Squeeze the wave tighter (high) or stretch it out (low). | 1 |
seed | Give each object its own wave. Same seed always picks the same wave. | 0 |
shift | true = auto-switch to a new random wave every few seconds. Smooth transitions. Different every page load. | false |
| Option | What it does | Default |
|---|---|---|
phase | Nudge the wave sideways. | 0 |
mode | 'stable' = clean. 'wild' = wobbly and unpredictable. | 'stable' |
unpredictability | Chaos dial for wild mode. 0 = calm. 1 = full chaos. | 0 |
mix | When blending two waves: 0 = all first wave, 1 = all second wave. | 0.5 |
shiftInterval | How long to hold each wave before switching (seconds). | 3 |
shiftDuration | How long the smooth transition takes (seconds). | 1 |
Use the name or the number. Yes, there's one called batman. Try them all in the Wave Lab.
This is the feature that makes people go "whoa". One flag, and your wave starts switching to random formulas on its own. Smooth morphs. Different sequence every time you reload.
Reload the page. Different waves. Every time. Your sketch is never the same twice.
Shift also works directly on Waves.wave() — just add shift: true to the options. But the sampler version is better: it caches everything and gives you the .waveName / .shifting / .mix getters for free.
Pick two waves. Slide between them. Connect mix to your mouse and you get a live crossfader.
mix: 0 = pure first wave. mix: 1 = pure second wave. Anything in between = a blend of both. Animate it for smooth shape transformations.
If you're calling the same wave 200 times per frame (particles, grid cells, trail points), set up a sampler once and call .sample() instead. Same result, less repeated work.
Takes all the same options as Waves.wave(). Including shift: true.
Want X and Y to move independently? Two samplers, two different seeds.
One call fills a 2D grid with wave values. Two waves combine — one runs along the rows, one along the columns. The result: animated black-and-white patterns.
| Option | What it does | Default |
|---|---|---|
waveRow | Wave for the rows. | random |
waveCol | Wave for the columns. | random |
seed | Auto-picks two different waves for you. | 0 |
range | Continuous output. Returns Float32Array. | null |
threshold | Binary on/off. Returns Uint8Array. | null |
speed | How fast the pattern moves. | 1 |
heads up The output array is reused between calls. If you need to keep a copy: new Float32Array(g.sample(t)).
Turn on mode: 'wild' and the wave gets wobbly. Frequency, phase, and amplitude all start drifting. It stays recognizable — just... unhinged.
Start at 0.3 and work your way up. At 1.0 things get interesting. Wild mode is about 5x slower though — so don't use it in a 10,000-point loop unless you're ready for that.
There's no built-in clock. You pass t on every call. That means you're in charge.
Want to pause? Stop changing t. Want to rewind? Decrease it. Want slow motion? Use a tiny increment. Want to sync two sketches? Give them the same t.
These look similar but do different things:
Index = "I want that exact wave." Seed = "Give each of my 50 particles its own consistent wave — I don't care which."
Waves.* always works. But in a regular p5 sketch, shorter names are available too.
| Always works | p5 global mode | p5 instance mode |
|---|---|---|
Waves.wave(y, opts) | waves(y, opts) | p.waves(y, opts) |
Waves.createSampler(opts) | createWaveSampler(opts) | p.createWaveSampler(opts) |
Waves.createGrid(c, r, opts) | createWaveGrid(c, r, opts) | p.createWaveGrid(c, r, opts) |
Extras: Waves.list() returns all 34 formulas. Waves.count = 34. Waves.data = the raw formula array.
Grab one. Drop it in your sketch. Go.