About
Pass a number in, get a number back. Behind that one promise lives a curated set of 35 wave shapes: smooth sines and sharp sawteeth, gentle bumps and noise-flecked chaos, each one a tiny formula tuned to drive motion, color, shape, or pattern in a sketch.
A Java twin
processing.waves is the Java sibling of p5.waves, a p5.js library originally built for the browser side of generative coding. The Java port carries the same vocabulary across to Processing 4: same names, same indices, same numerical output. If you sketch in p5.js today and in Processing tomorrow, you bring the same building blocks with you.
Processing 4 has been a home for creative coders for two decades. It runs offline, exports to native apps, and handles pixel-by-pixel work without browser overhead. Artists, educators and students live there. Until now, the wave vocabulary from p5.waves stopped at the JavaScript boundary; this port hands those users the same set of shapes, behaving the same way, in the language they already use.
The challenge of a faithful port
Porting between two languages with similar surface syntax is usually a chore: rename, retype, recompile. Making the output of two implementations match is something else entirely. Floating-point math is full of subtle traps: signed vs unsigned 32-bit ints, hash functions that wrap differently across languages, pseudo-random sequences that drift apart by the fifth decimal. processing.waves takes those traps seriously:
FNV-1a 32-bit hash, returning the unsigned uint32 as a Java
longso the subsequent float math matches JS's unsigned-Number semantics.mulberry32 PRNG, ported bit-for-bit. A given seed produces the same sequence on both sides of the language border.
double-precision internal math, narrowed to
floatonly at the public API boundary, matching JS Numbers exactly where it counts and meeting the Processing convention everywhere else.
Trust, then verify
To prove the port works, the repo ships a numerical validator. It runs the JS reference (loaded via Node), captures 49 input/output pairs across the wave registry and every code path, then runs the Java port over the same inputs and asserts equality within tolerance.
| Metric | Value |
|---|---|
| Cases | 49 (representative waves, every option path, and the group pools) |
| Stable-mode tolerance | 1e-3 |
| Wild-mode tolerance | 0.5 |
| Current status | 49 / 49 pass |
Wild mode's looser tolerance is intentional: noise modulation amplifies tiny float-vs-double differences. The wave envelope still matches across the languages; only the per-sample chaos detail diverges within the noise budget. Visually you cannot tell them apart.
35 shapes, four pools
Every wave has a character, and the library groups them so you can lean into the one you want:
Gentle: calm and bounded. Sines, cosines and modulos, plus the sharp-but-periodic shapes like
squareandpulse— hard edges, dependable rhythm.Harsh: breaks rhythm. Tangent-spiked or noise-driven, but also the ones that simply refuse to stay calm —
bald patchaccelerates without bound,half sinekeeps changing its stride.Closing: the set whose period divides 2π·n cleanly. A shape drawn over a full cycle joins back to itself without a seam, which is what you want when you sweep a wave around a circle.
Ghost: six closing waves hand-picked to stay clean when a wave is plotted against a delayed copy of itself — that pairing draws the wave's phase portrait as a closed loop.
You don't have to pick by hand. Ask the library for "a gentle one" and let a seed decide. Ask for a specific name. Ask for two names and crossfade between them with mix. Flip shift on and watch the library cycle through random formulas at a tempo you set. All from one function call.
How it differs from its parent
Fluent builder instead of object literal. Java doesn't have JS-style options objects, so
WaveOptsuses chained setters:new WaveOpts().wave("triangle").amplitude(80).No runtime string evaluator. JS p5.waves uses
new Function(algoString)internally to compile each wave's formula. Java replaces this with hand-written lambdas, one per wave. The string-eval feature was never exposed via the public API, so there's no surface-level difference.Pure math, no browser glue. No DOM, no canvas wrappers, no event handlers. The library plugs into Processing's draw loop like any other math utility.
License & credits
processing.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 all welcome on GitHub.
Makers & Contributors
Maker
Contributors (wave formula dataset)
tw@GenerativePunk — original dataset contributor
gh@ffd8 (Ted Davis) — original dataset contributor, Oscillation Sandbox
References
titanwolf.org — pulse / rectangular wave formulas
p5.js editor — jeremydouglass triangle sketch