npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sebastienrousseau/sine-wave-generator

v0.0.3

Published

A JavaScript library for generating animated sine waves on a canvas.

Readme

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.


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).


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.


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.