@redesigner/wave.js
v1.3.0
Published
GPU-accelerated animated sine wave backgrounds. Vanilla JS + React. WebGL2 → Canvas 2D → CSS fallback. 60 FPS.
Maintainers
Readme
wave.js
GPU-accelerated animated sine wave backgrounds. Works with vanilla JS and React. Built with raw WebGL2 + custom GLSL shaders. Automatic fallback chain: WebGL2 → Canvas 2D → CSS gradient → solid color.
Full documentation: DOCS.md
Features
- Zero dependencies for vanilla JS — no Three.js, no React required
- WebGL2 GPU rendering at 60 FPS
- Automatic fallback: WebGL2 → Canvas 2D (CPU) → CSS gradient (static) → solid color (none)
- User-selectable renderer via
rendereroption orsetRenderMode()at runtime - 12 adjustable parameters (waves, speed, amplitude, frequency, opacity, thickness, blur, concentration, randomness, thickness randomness, vertical offset, rotation)
- 6 built-in color themes with automatic time-of-day selection
- Custom RGBA color picker with per-color opacity
- Glass effect, Liquid Metal effect, Split Fill mode, Bloom, Lumen, Twist
- Rotation (0–360°) around screen center
- Mouse-reactive wave distortion
- Smooth 1500ms color transitions between themes
- Film grain post-processing (WebGL only)
- React component with built-in control panel
- Responsive on mobile
- Retina / HiDPI support (capped at 2x)
Installation
npm install @redesigner/wave.jsVanilla JS
import { WaveBackground } from '@redesigner/wave.js'
const wave = new WaveBackground('#hero', {
theme: 'sunset',
waveCount: 12,
speed: 0.5,
})Force a specific renderer
// Force Canvas 2D (no GPU)
const wave = new WaveBackground('#hero', {
renderer: 'canvas2d',
})
// Force no effects at all
const wave = new WaveBackground('#hero', {
renderer: 'none',
theme: 'night',
})Switch renderer at runtime
wave.setRenderMode('canvas2d') // Switch to CPU rendering
wave.setRenderMode('css') // Static gradient
wave.setRenderMode('none') // Solid background color
wave.setRenderMode('webgl2') // Back to GPUUpdate parameters
wave.setParam('waveCount', 20)
wave.setParam('amplitude', 0.1)
wave.setParam('rotation', 45)
wave.setTheme('night')
wave.setColors(['#ff0000', '#00ff00', '#0000ff', '#ffff00'])
wave.setSplitFill(true)
wave.setGlass(true)
wave.setLiquidMetal(true)Cleanup
wave.destroy()Plain HTML (no bundler)
<div id="hero" style="width: 100%; height: 100vh;"></div>
<script type="module">
import { WaveBackground } from '@redesigner/wave.js'
new WaveBackground('#hero', { theme: 'daytime' })
</script>React
npm install @redesigner/wave.js react react-domimport { HeroWave } from '@redesigner/wave.js/react'
function App() {
return (
<HeroWave theme="sunset">
<h1>Your content here</h1>
</HeroWave>
)
}The React component includes a built-in control panel with all sliders, color picker, effect toggles, and renderer selector.
Available props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| theme | string | auto (time-of-day) | Color theme name |
| style | object | {} | Container inline styles |
| className | string | — | Container CSS class |
| children | ReactNode | — | Content rendered on top of the wave background |
API Reference
new WaveBackground(container, options?)
Creates an animated wave background in the given container.
container — DOM element or CSS selector string (e.g. '#hero').
options:
| Option | Default | Description |
|--------|---------|-------------|
| renderer | 'auto' | Renderer to use: 'auto', 'webgl2', 'canvas2d', 'css', or 'none' |
| theme | auto (time-of-day) | Color theme: 'pre-dawn', 'sunrise', 'daytime', 'dusk', 'sunset', 'night' |
| waveCount | 8 | Number of wave layers (1–100) |
| speed | 0.3 | Animation speed (0–2) |
| amplitude | 0.06 | Wave height (0–0.2) |
| frequency | 2.5 | Wave density (0.5–10) |
| opacity | 0.6 | Wave transparency (0–1) |
| thickness | 1 | Wave solid core width in px (1–100) |
| blur | 30 | Edge fade zone in px (0–200) |
| concentration | 0 | Vertical compression toward center (0–50) |
| randomness | 0 | Per-wave amplitude variation (0–1) |
| thicknessRandom | 0 | Per-wave thickness variation (0–1) |
| verticalOffset | 0 | Shift waves up/down (-0.5–0.5) |
| rotation | 0 | Rotation in degrees (0–360) |
| splitFill | false | One-directional fill mode |
| glass | false | Glass transparency effect (WebGL only) |
| liquidMetal | false | Chrome/metal effect (WebGL only) |
| lmLiquid | 0.07 | Liquid Metal flow intensity (0–0.2) |
| bloom | false | HDR bloom post-processing (WebGL only) |
| bloomThreshold | 0.6 | Luminance above which bloom kicks in (0–1) |
| bloomIntensity | 1.4 | Bloom halo strength (0–3) |
| lumen | false | Glowing-ribbon render mode (WebGL only) |
| lumenIntensity | 1 | Lumen brightness multiplier (0–2). >1 drives a stronger bloom halo. |
| twist | false | 3D chrome/glass twisted-ribbon effect (WebGL only) |
| twistAmount | 1 | Twist intensity (0–1) |
| colors | — | Explicit 4-hex-color array. Overrides theme. |
| colorOpacities | [1,1,1,1] | Per-color opacity array |
Methods
| Method | Description |
|--------|-------------|
| setRenderMode(mode) | Switch renderer: 'webgl2', 'canvas2d', 'css', or 'none' |
| setTheme(name) | Switch color theme with 1500ms animated transition |
| setColors(hexArray) | Set 4 custom hex colors with animated transition |
| setParam(key, value) | Update any wave parameter instantly |
| setColorOpacities(arr) | Set per-color opacity [0-1, 0-1, 0-1, 0-1] |
| setSplitFill(bool) | Toggle split fill mode |
| setGlass(bool) | Toggle glass effect |
| setLiquidMetal(bool) | Toggle liquid metal effect |
| setBloom(bool) | Toggle HDR bloom post-processing |
| setLumen(bool) | Toggle glowing-ribbon render mode |
| setTwist(bool) | Toggle 3D chrome twist effect |
| toJSON() | Return current settings as a plain object (round-trips through new WaveBackground(el, json)) |
| setConfig(obj) | Apply a settings object at runtime — mirror of what the constructor accepts |
| destroy() | Stop animation, remove canvas, cleanup all event listeners |
Properties
| Property | Description |
|----------|-------------|
| renderMode | Current active renderer: 'webgl2', 'canvas2d', 'css', or 'none' |
| params | Current parameter values object |
| theme | Current theme name |
Renderers
| Mode | Description | GPU | Animated | Effects |
|------|-------------|-----|----------|---------|
| webgl2 | Full GPU shader rendering | Yes | Yes | All (glass, liquid metal, film grain) |
| canvas2d | CPU-based line drawing | No | Yes | Waves, colors, opacity, rotation |
| css | Static CSS gradient | No | No | Theme colors as gradient |
| none | Solid background color | No | No | Background color only |
When renderer is set to 'auto' (default), the fallback chain is:
WebGL2 available? → GPU shader (60 FPS, all effects)
↓ no
Canvas 2D available? → CPU rendering (animated waves)
↓ no
CSS gradient (static theme colors)You can check which renderer is active via wave.renderMode.
Themes
| Theme | Auto Time | Colors |
|-------|-----------|--------|
| pre-dawn | 05:00–08:00 | Deep purple, magenta, orange, gold |
| sunrise | 08:00–11:00 | Dark purple, hot pink, orange, yellow |
| daytime | 11:00–16:00 | Navy, blue, cyan, mint |
| dusk | 16:00–20:00 | Dark purple, violet, lavender, light purple |
| sunset | 20:00–23:00 | Deep purple, pink, coral, orange |
| night | 23:00–05:00 | Near-black, dark purple, medium purple, violet |
When no theme is specified, the component automatically selects based on the user's local time and re-checks every 60 seconds.
To disable auto-detection, simply pass a theme option:
// Fixed theme — no auto-switching
new WaveBackground('#hero', { theme: 'sunset' })
// React — fixed theme
<HeroWave theme="sunset">...</HeroWave>If the user manually selects a theme via the control panel, auto-detection is disabled until reset.
JSON config
Everything you tune in the playground at wavejs.org is just options — and the constructor accepts them all. Two flows:
A. Inline options (no JSON):
new WaveBackground('#hero', {
theme: 'sunset', waveCount: 12, bloom: true, twist: true,
})B. Export + load JSON. Tweak the playground to taste, click Copy JSON
in the Parameters panel, paste into a config.json, and load at runtime:
// vanilla
const config = await fetch('/config.json').then(r => r.json())
const wave = new WaveBackground('#hero', config)// React
import config from './config.json'
const wave = new WaveBackground(el, config)The JSON shape matches the options 1-to-1 — no mapping layer:
{
"renderer": "webgl2",
"colors": ["#07070f", "#3730a3", "#06b6d4", "#34d399"],
"colorOpacities": [1, 1, 1, 1],
"waveCount": 4, "speed": 0.3, "amplitude": 0.08, "frequency": 2,
"thickness": 50, "blur": 6, "opacity": 1,
"bloom": false, "twist": true, "twistAmount": 1
}Round-trip at runtime: wave.toJSON() returns the current settings; wave.setConfig(obj) applies one. See examples/vanilla/from-json.html and examples/react/src/AppFromJson.jsx for working samples.
Browser Support
- Chrome 56+
- Firefox 51+
- Safari 15+
- Edge 79+
Fallback renderers ensure the component works even in environments without WebGL.
Build
npm run build