Skip to content

Latest commit

 

History

97 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Sine Wave Generator logo

Sine Wave Generator — Smooth Canvas Animation

Build Version License npm Bundle size Last Commit


Overview

Render animated sine waves on a canvas element with minimal configuration. Sine Wave Generator is a performance-focused JavaScript library that draws smooth, layered waveforms at a steady 60 fps with zero dependencies.

The library ships at roughly 3 KB gzipped. It uses requestAnimationFrame for battery-efficient rendering, caps pixel ratios for memory control, and supports pointer interactions out of the box.


Table of contents


Key features

Core capabilities

  • Fast rendering. Segment-based drawing with capped pixel ratios keeps frame budgets tight.
  • Flexible configuration. Tune amplitude, wavelength, speed, and easing to match any visual style.
  • Layered waves. Stack multiple waves with independent settings for depth and parallax.
  • Responsive sizing. Element-bound dimensions with device pixel ratio support and a configurable cap.
  • Pointer control. Built-in mouse and touch tracking adjusts wave phase in real time.
  • Audio-reactive sync. Bind a live audio source so wave amplitude, speed, and rotation react to beats, tempo, and frequency energy.

Performance

  • Memory control. maxPixelRatio limits the offscreen buffer size on high-DPI screens.
  • Adjustable detail. segmentLength controls point density for smooth or fast rendering.
  • Efficient redraws. A single animation loop with cached gradients minimises per-frame overhead.

Get started

Installation

npm install @sebastienrousseau/sine-wave-generator
# or
yarn add @sebastienrousseau/sine-wave-generator
# or
pnpm add @sebastienrousseau/sine-wave-generator

Requirements

  • Browser: any evergreen browser with Canvas 2D support (Chrome, Firefox, Safari, Edge). AudioSync additionally requires the Web Audio API (supported in all evergreen browsers).
  • Node.js: >=16 — only relevant for the build/test tooling in this repo; the published package is plain browser JavaScript with zero runtime dependencies.
  • No bundler or build step is required to consume the library: drop src/sine-wave-generator.js in with a <script> tag, or require/import it directly.

Basic usage

<canvas id="sine"></canvas>
<script src="./node_modules/@sebastienrousseau/sine-wave-generator/src/sine-wave-generator.js"></script>
<script>
	const generator = new SineWaveGenerator({
		el: "#sine",
		maxPixelRatio: 2,
		waves: [{ amplitude: 26, wavelength: 120, speed: 0.8 }],
	});

	generator.start();
</script>

Module usage (CommonJS or ESM)

// CommonJS
const { SineWaveGenerator } = require("@sebastienrousseau/sine-wave-generator");
// ESM
import { SineWaveGenerator } from "@sebastienrousseau/sine-wave-generator";
const generator = new SineWaveGenerator({
	el: "#sine",
	maxPixelRatio: 2,
	waves: [{ amplitude: 26, wavelength: 120, speed: 0.8 }],
});

generator.start();

AudioSync is available the same way from the /audio-sync subpath:

const {
	AudioSync,
} = require("@sebastienrousseau/sine-wave-generator/audio-sync");
// or: import { AudioSync } from "@sebastienrousseau/sine-wave-generator/audio-sync";

The package ships both a CommonJS and a native ESM build (resolved automatically via package.json exports), plus bundled TypeScript types. The deep-import paths used in older versions of these docs — require("@sebastienrousseau/sine-wave-generator/src/sine-wave-generator.js") and .../src/audio-sync.js — still work unchanged; they resolve straight to the unbundled source.

Set strokeStyle to null to use the built-in gradient stroke.

Sync animation to audio (BPM-reactive)

Bind an AudioSync instance to a SineWaveGenerator so wave parameters react to music in real time — amplitude pulses with bass, speed tracks overall energy, and beats trigger a short amplitude boost.

const { SineWaveGenerator } = require("@sebastienrousseau/sine-wave-generator");
const {
	AudioSync,
} = require("@sebastienrousseau/sine-wave-generator/audio-sync");

const generator = new SineWaveGenerator({
	el: "#sine",
	waves: [{ amplitude: 20, wavelength: 120, speed: 0.5 }],
});

const audioSync = new AudioSync(); // or new AudioSync({ bpm: 128 }) for a known tempo
const audioEl = document.querySelector("audio");
audioEl.addEventListener(
	"play",
	() => {
		audioSync.connect(audioEl); // also accepts a MediaStream, e.g. from getUserMedia()
		generator.syncToAudio(audioSync);
	},
	{ once: true },
);

generator.start();

Pass a custom mapping as the second argument to syncToAudio() to control which metric ("energy", "bass", "mid", "treble") drives which wave property (amplitude, speed, rotate), and how strongly:

generator.syncToAudio(audioSync, {
	amplitude: { source: "bass", intensity: 2 },
	speed: { source: "energy", intensity: 1 },
	rotate: { source: "treble", intensity: 0.5 },
});

Call generator.unsyncAudio() to detach and restore each wave's original amplitude, speed, and rotation.

Back to Top


API reference

Constructor

new SineWaveGenerator(options)

Option Type Description Required
el HTMLCanvasElement | string Canvas element or CSS selector Yes
waves WaveConfig[] Initial wave configurations No
pixelRatio number Override device pixel ratio. Omit to track it automatically, including live display changes No
maxPixelRatio number Cap pixel ratio for memory control No
autoResize boolean Auto-resize on canvas box changes (ResizeObserver) and window resize No
respectReducedMotion boolean Honor prefers-reduced-motion by scaling animation speed down. Defaults to true No
reducedMotionScale number Speed multiplier while reduced motion is preferred. Defaults to 0.25; set to 0 to fully pause No
ariaLabel string | null Accessible label for the canvas (sets role="img"). Omit for decorative canvases (aria-hidden) No
colorScheme "auto" | "light" | "dark" Default gradient palette. "auto" follows prefers-color-scheme live. Defaults to "auto" No

WaveConfig

Property Type Default Description
phase number Random Phase offset in radians
speed number Random 0.5 -- 1.0 Animation speed multiplier
amplitude number 10 Wave height in pixels
wavelength number 100 Peak-to-peak distance in pixels
strokeStyle string | null null CSS colour or null for gradient
segmentLength number 10 Point density (lower is smoother)
easing function Ease.sineInOut Easing curve for wave shape
rotate number 0 Rotation angle in degrees (0 -- 359)

Instance methods

Method Description
start() Start the animation loop
stop() Stop the animation loop and unbind events
resize() Recalculate canvas size and rebuild gradients
addWave(config) Add a new wave at runtime
removeWave(index) Remove a wave by index
bindEvents() Bind resize, mouse, touch, and responsiveness/accessibility listeners
unbindEvents() Unbind all events and listeners
syncToAudio(audioSync, mapping?) Bind an audio source's live metrics to wave parameters
unsyncAudio() Detach the bound audio source and restore original wave values

A high maxPixelRatio on large canvases will increase memory use proportionally.

Accessibility & responsiveness

By default, SineWaveGenerator:

  • Marks the canvas aria-hidden="true" (it's decorative by default) unless you pass ariaLabel, in which case it sets role="img" and that label instead — set your own aria-* attributes on the element beforehand to opt out.
  • Scales animation speed to reducedMotionScale (default 0.25) when the user has prefers-reduced-motion enabled, and updates live if that preference changes. Pass respectReducedMotion: false to disable, or reducedMotionScale: 0 to fully pause instead of slowing down.
  • Tracks devicePixelRatio live via a matchMedia listener when pixelRatio isn't explicitly set, so moving the window to a display with different pixel density stays sharp.
  • Observes the canvas element itself with ResizeObserver (in addition to the window resize event) when autoResize is true, so layout-driven size changes — not just window resizes — are picked up automatically.
  • Picks the default gradient's palette from prefers-color-scheme (a cooler, higher-contrast palette for dark backgrounds) and updates live if the OS/browser theme changes, when using the built-in gradient (strokeStyle: null). Pass colorScheme: "light" or "dark" to force a palette instead of following the system preference.
const generator = new SineWaveGenerator({
	el: "#sine",
	ariaLabel: "Ambient background animation",
	reducedMotionScale: 0, // fully pause instead of slowing down
});

AudioSync

new AudioSync(options?) — analyzes an HTMLMediaElement or MediaStream with the Web Audio API and derives real-time metrics for syncToAudio().

Option Type Default Description
fftSize number 1024 FFT size for the analyser (must be power of 2)
smoothingTimeConstant number 0.8 Analyser smoothing, 0--1
bpm number | null null Manual tempo override; omit to auto-detect
Method Description
connect(source) Connect an HTMLMediaElement or MediaStream
disconnect() Disconnect and reset analysis state
update(timestampMs) Sample the source and refresh metrics
getMetrics() Return the last computed metrics without sampling

Metrics returned by update()/getMetrics(): energy, bass, mid, treble (all normalized 0--1), beat (boolean, true on the detected frame), beatPhase (0--1 progress through the current beat), and bpm (manual or auto-detected tempo, or null if unknown).

Beat detection is a lightweight heuristic, not a validated DSP algorithm. It's a variance-thresholded energy detector on the bass band alone — cheap enough to run once per animation frame, but it under-detects material whose rhythm isn't bass-driven (ambient, classical, sparse/syncopated percussion), and only reports a bpm once two or more beats land 60--200 BPM apart. See the detectBeat() JSDoc in src/audio-sync.js for the full algorithm basis and limitations. For more robust detection, pass a known bpm manually, or pair AudioSync with a dedicated analysis library (e.g. realtime-bpm-analyzer, web-audio-beat-detector, or Meyda for richer spectral features) and feed its output through a custom object exposing update(timestampMs).

Back to Top


React

An optional useSineWaveGenerator hook is available from the /use-sine-wave-generator subpath (raw source, not bundled — react is a peer dependency, only required if you import this). It creates the generator on mount, starts it, and destroys it on unmount.

import { useSineWaveGenerator } from "@sebastienrousseau/sine-wave-generator/use-sine-wave-generator";

function AmbientBackground() {
	const { canvasRef } = useSineWaveGenerator({
		waves: [{ amplitude: 20, wavelength: 120, speed: 0.5 }],
		ariaLabel: "Ambient background animation",
	});
	return <canvas ref={canvasRef} style={{ width: "100%", height: "100%" }} />;
}

The hook is intentionally thin: it creates the generator once from the options passed on the first render (a new waves array on a later render calls setWaves() automatically), and returns generatorRef as an escape hatch — call any instance method on generatorRef.current (addWave, syncToAudio, setQualityPreset, ...) for anything else you need to update imperatively.

Back to Top


Examples

Open docs/index.html for a full interactive demo covering:

  • Fundamental wave controls (amplitude, wavelength, easing, pause/resume)
  • Pattern examples (pulse matrix, DNA helix, fluid column, Lissajous orbits)
  • Advanced modes (Moire interference, kinetic typography, damped sine)
  • A live playground with presets and configurable parameters

TypeScript

Type definitions ship with the package.

import {
	SineWaveGenerator,
	Wave,
	Ease,
	WaveConfig,
	SineWaveGeneratorOptions,
	ValidationError,
	CanvasError,
} from "@sebastienrousseau/sine-wave-generator";
import {
	AudioSync,
	AudioSyncOptions,
	AudioMapping,
	AudioMetrics,
	AudioSyncError,
} from "@sebastienrousseau/sine-wave-generator/audio-sync";

Every error thrown by this library is one of ValidationError, CanvasError, or AudioSyncError (all extend Error), so you can discriminate failure modes with instanceof instead of matching on message strings:

try {
	new SineWaveGenerator({ el: "#missing-canvas" });
} catch (error) {
	if (error instanceof CanvasError) {
		// canvas element or its 2D context is missing/unusable
	}
	throw error;
}

AI tools & MCP

This project publishes llms.txt and llms-full.txt (the llmstxt.org convention) — a structured, link-indexed summary and a full-content reference respectively, meant for LLMs and AI coding assistants to consume at inference time rather than crawling the whole site.

To expose these directly to an MCP-capable assistant (Claude Desktop, Claude Code, Cursor, Windsurf), point a generic MCP documentation server such as mcpdoc at the llms.txt URL instead of building or running anything project-specific. A typical MCP client config looks like:

{
	"mcpServers": {
		"mcpdoc": {
			"command": "uvx",
			"args": [
				"--from",
				"mcpdoc",
				"mcpdoc",
				"--urls",
				"SineWaveGenerator:https://sine-wave-generator.com/llms.txt"
			]
		}
	}
}

Check mcpdoc's own docs for the current invocation flags and where your specific client expects this config — it may change between mcpdoc versions.

Note: neither llms.txt adoption nor MCP exposure is proven to affect search ranking or how often an AI cites this project — treat both as low-cost hygiene for tools that respect them, not as an SEO or marketing lever.


Contributing

Please read CONTRIBUTING.md before opening a pull request.

For security issues, see SECURITY.md.


License

This project is licensed under the Apache-2.0 License. See LICENSE.

About

An enhanced sine wave generator tailored for web applications, offering advanced features for creating dynamic, visually captivating sine waves. Ideal for educational tools, music production software, and creative web projects.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

17 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages