about
Pass a number in, get a number back. vanilla.waves has 35 wave formulas you can use for motion, colour, shape or pattern, plus a DOM engine for HTML elements.
The math
vanilla.waves is the plain-DOM sibling of p5.waves. Look at the p5.waves source: wave(), createSampler(), the 35 formulas, the seeded shift/morph engine - all of it is pure Math.*. The only p5 touchpoint is an 8-line prototype hook at the very bottom of the file.
The port keeps the p5.waves math and exposes it through VanillaWaves. Its DOM engine can use those values to update loaders, backgrounds, text fields or meters.
Lineage. p5.waves → p5 / canvas · processing.waves → Java / Processing · vanilla.waves → plain DOM, zero deps. Same dialect, different runtime - processing.waves is the precedent for "same language, maintained apart."
What the port preserves
The source is JavaScript, so the port keeps the original math rather than translating the formulas. These are the parts it preserves:
Byte-for-byte core.
waves-core.jsis the canonicalp5.waves.jsmath with only the 8 p5-prototype lines removed. The port preserves the formulas rather than rewriting them.Own global, drop-in name. The library exposes
VanillaWaves(aliasvWaves), and answers toWavesas a drop-in when p5.waves isn't present.Faithful entropy.
shiftmixes in a per-session random offset, so the same seed yields a different sequence each page load - exactly as p5.waves does. There is no option to fix the sequence across page loads.
Parity checks
The comparison recorded on 2026-07-16 checked the port against p5.waves v3.6.0. The current check-parity.mjs checks the generated bundles against waves-core.js, with 8,620 comparisons. These checks cover the tested inputs; they do not guarantee that future changes will agree.
| Metric | Value | |
|---|---|---|
| Parity checks | 10,422 on 2026-07-16 (all 35 waves + morph, range, wild, samplers, shift) | |
| Result | bit-identical - 10,422 / 10,422 pass | |
| Baseline | p5.waves v3.6.0 (commit 804f91e, 35 waves) | |
| Drift watch | Drift-watch procedure; runs while the Claude app is open | |
See waves.feature.md for the port-coverage and drift-watch changedoc.
35 shapes, four pools
Every wave has a character. The library splits them into four pools so you can lean into the one you want:
Gentle (26): pure functions of x. Deterministic, repeatable, made of sines, cosines and modulos.
Harsh (9): noise-driven or tangent-spiked. They pull from a seeded PRNG, so they jitter and surprise within the wave's envelope.
Closing (19): a pool that shares one base period, so a shape swept over an integer number of periods joins back to itself without a seam, and stays closed even while
shiftmoves to the next formula.Ghost (6): a curated subset of closing that still reads clean when you plot a wave against a delayed copy of itself.
Choose a wave by name, let a seed choose from a group, blend two waves with mix, or enable shift to move between formulas at the interval you set.
The DOM engine
No canvas, no p5. There is no drawing surface. The math returns numbers; you map them onto DOM - a bar height, a character, an opacity, an SVG point.
A tiny DOM engine included. Register an element type, mark it with
data-wv, and one shared 30 fps loop animates it. Offscreen elements pause viaIntersectionObserver;prefers-reduced-motionrenders one static frame instead of animating.Core stays pure. The engine augments the same global but never touches the math. Element libraries import the core; the core never imports them.
Where the components live
This repository contains the math and DOM engine. Components such as loaders, controls and draggables live in a separate package, vanilla.waves_elements, built on this engine.
License & credits
vanilla.waves is MIT, like its parent. The original p5.waves library lives at seb-prjcts-be/p5.waves. Bug reports, sketches built with the library, and pull requests are welcome on GitHub.
Maker
Contributors (wave formula dataset)
tw@GenerativePunk - original dataset contributor
gh@ffd8 (Ted Davis) - original dataset contributor, Oscillation Sandbox