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

seinx-nn-canvas-animation

v3.3.0

Published

Neural network canvas animation web components — by SeinX

Readme

Neural Canvas Animation

A breathing neural network animation for the web — two web components, zero dependencies, one CDN tag.

v3.2.0 | Live demo | Built by SeinX


Quick Start

CDN (one tag)

<!DOCTYPE html>
<html>
<body style="margin:0; background:#1a1a1a;">
  <neural-canvas style="width:100vw; height:100vh; display:block;"></neural-canvas>
  <script type="module" src="https://unpkg.com/seinx-nn-canvas-animation/dist/neural-canvas.min.js"></script>
</body>
</html>

That's it. Save it as an HTML file and open it.

npm

npm install seinx-nn-canvas-animation
// Canvas only (landing page background)
import { NeuralCanvas } from 'seinx-nn-canvas-animation';

// Canvas + controls (tinkering UI)
import { NeuralCanvas } from 'seinx-nn-canvas-animation/canvas';
import { NeuralCanvasControls } from 'seinx-nn-canvas-animation/controls';

What You Get

<neural-canvas>

Shadow DOM headless renderer. Two overlapping neural networks animate on canvas — nodes drift, connections breathe, halos glow. No UI chrome, just the animation.

Attributes:

| Attribute | Purpose | |-----------|---------| | preset-data | Base64-encoded 41-parameter preset string | | paused | Pause the animation loop (boolean attribute) | | no-attr | Skip the SeinX attribution overlay | | no-brake | Disable the auto safety brake (boolean attribute) | | uniform-random | Switch reshuffle() + cold-load to full-uniform sampling instead of family-based |

Methods:

const canvas = document.querySelector('neural-canvas');

// Apply a full preset from base64
canvas.applyPreset('eyJub2RlRGVuc2l0eSI6MTMy...');

// Tweak individual parameters (partial config)
canvas.applyConfig({ speed: 0.05, glowSpread: 3.0 });

// Randomize with the family-based randomizer
canvas.reshuffle();

// Nudge a vibe dimension (strength: -2, -1, 1, or 2)
canvas.nudgeVibe('depth', 2);

// Get current state as a shareable base64 string
const preset = canvas.getPresetData();

// Crossfade to a random preset over 3 seconds
canvas.morphToRandom(3000);

// Force a clean re-render (useful after external DOM changes)
canvas.rerenderFresh();

Events:

| Event | Payload | When | |-------|---------|------| | neural-canvas:stats | { fps, frameTime, nodeCount, connectionCount, breathPhase } | Every frame | | neural-canvas:ready | — | Component initialized, first frame rendered | | neural-canvas:low-fps | { fps, brakeEngaged } | Sustained low FPS detected (brake engaged if _safetyBrakeEnabled) |

Accessibility:

canvas.reducedMotion = true;   // Freeze all motion (also auto-detects OS preference)
canvas.highContrast = true;    // Override opacities to 1.0, kill glow
canvas.safetyBrake = false;    // Disable the auto FPS-triggered reduced-motion latch
canvas.uniformRandom = true;   // reshuffle() samples uniformly instead of from families

<neural-canvas-controls>

The tinkering panel. Binds to a <neural-canvas> via for attribute or DOM proximity.

Contains: 10 vibe buttons, 41 parameter sliders, 3 preset slots, 2 color pickers, stats display, fullscreen toggle.

Mobile (viewport <= 768px): The desktop panel is hidden. A single frosted-glass RANDOM button appears instead — tap it to reshuffle. That's the only mobile affordance.

Keyboard shortcuts:

| Key | Action | |-----|--------| | Space | Reshuffle (randomize) | | S | Save to current preset slot | | Ctrl+C / Ctrl+V | Copy / paste preset as base64 | | Y | Toggle panel visibility | | P | Toggle stats overlay | | Ctrl+H / ⌘H | Toggle high contrast | | Ctrl+J / ⌘J | Toggle reduced motion | | Ctrl+B / ⌘B | Toggle safety brake | | Z | Toggle screensaver mode (includes fullscreen) | | 1 / 2 / 3 | Load saved preset slot | | Escape | Exit screensaver if active, otherwise blur active element |

Screensaver Mode

Press Z, click the screensaver button, or call controls.toggleScreensaver() from JavaScript. The animation:

  1. Hides the controls element (panel, screensaver button, mobile RANDOM FAB) and the Built by SeinX attribution — clean kiosk view
  2. Requests fullscreen (granted only on user gesture; falls back gracefully if denied)
  3. Crossfades to a new random preset every 30 seconds (configurable, 30s–15m)
  4. Each crossfade takes 5 seconds (configurable, 1–15s) using morphToRandom()
  5. The cursor auto-hides after 3 seconds of inactivity
  6. Press Z or Escape to exit — controls and attribution restore to their pre-screensaver state

?screensaver URL parameter (kiosk launch)

index.html accepts ?screensaver (also ?screensaver=1, ?screensaver=true) to drop straight into screensaver mode on page load. Useful for digital-signage / kiosk / shareable demo links:

https://nn.seinx.ai/?screensaver
https://nn.seinx.ai/?preset=<base64>&screensaver

Note: load-triggered screensaver does not engage fullscreen (browsers require a user gesture). The clean kiosk visual still applies.

Under the hood, the crossfade system uses 4 canvas layers — two live canvases and two overlay canvases for smooth transitions. The old frame is painted to an overlay, the new preset starts rendering underneath, and the overlay fades out.


Examples

Landing page background

<style>
  body { margin: 0; overflow: hidden; }
  neural-canvas { position: fixed; inset: 0; z-index: -1; }
</style>
<neural-canvas></neural-canvas>
<main style="position: relative; color: white; padding: 2rem;">
  <h1>Your content here</h1>
</main>

Full interactive page

<neural-canvas id="bg"></neural-canvas>
<neural-canvas-controls for="bg"></neural-canvas-controls>
<script type="module" src="https://unpkg.com/seinx-nn-canvas-animation/dist/neural-canvas.min.js"></script>

Tweak from JavaScript

const canvas = document.querySelector('neural-canvas');

// Make it breathe faster with stronger glow
canvas.applyConfig({
  breathSpeed: 0.8,
  glowSpread: 3.5,
  glowIntensity: 0.7,
});

// Push toward more organic visuals
canvas.nudgeVibe('organic', 2);

// Randomize everything
canvas.reshuffle();

Share a preset

// Export
const preset = canvas.getPresetData();
const url = `https://nn.seinx.ai?preset=${encodeURIComponent(preset)}`;

// Import
canvas.applyPreset(presetFromUrl);

// Or via HTML attribute
// <neural-canvas preset-data="eyJub2RlRGVuc2l0eSI6MTMy..."></neural-canvas>

Parameters (41)

Full reference: docs/parameters.md

| Category | Parameters | |----------|-----------| | Structure | nodeDensity, nodeSpread, connDist | | Density dynamics | nodeDensityVar, densityPull, densityJitter, nodeSpawnRate, nodeFadeTime | | Appearance | curveSoftness, nodeSize, nodeSizeVar, lineOpacity, nodeOpacity, glowSpread, glowIntensity, lineWidth, lineWidthVar | | Motion | speed, breathIntensity, breathSpeed, curveSpeed | | Breath sync | breathDistSync, breathCurveSync, breathSizeSync, breathOpacitySync, breathLineWidthSync, breathGlowSync, breathSpawnSync, breathDepthSync, breathLineStyleSync | | Breath shape | breathEnvelope (sine, triangle, exponential, sawtooth) | | Colors | net1Color, net2Color, bgColor | | Hub & Line Style | hubEmphasis, lineStyle, lineStyleAmount | | Depth | depthAmount, depthSpeedRatio | | Per-network | lineWidthLengthBias, lineWidth2 |


Line Styles (5 modes)

Set via lineStyle (integer 0–4). Only takes effect when lineStyleAmount > 0.

| Mode | Name | Character | |------|------|-----------| | 0 | Uniform | Standard straight or curved connections. Clean and even. | | 1 | Bowtie | Thick at endpoints, thin in the middle. Connections taper inward. | | 2 | Tube | Thin at endpoints, thick in the middle. Connections bulge outward. | | 3 | Wave | Sinusoidal displacement along the connection path. Organic ripple. | | 4 | Glow | Luminous filament rendering. Neon-like quality. |

lineStyleAmount controls intensity (0 = off, 1 = full). Can pulse with breathing via breathLineStyleSync.


Vibes (10)

Vibes are high-level nudges across multiple parameters. Call canvas.nudgeVibe(id, strength) where strength is -2, -1, 1, or 2.

| ID | Label | What it nudges | |----|-------|----------------| | energy | Energy | speed, curveSpeed | | density | Density | nodeDensity, connDist, densityPull | | organic | Organic | curveSoftness, nodeSizeVar, nodeSpread, lineWidthVar, lineStyleAmount | | breathing | Breathing | breathSpeed, all breath syncs, densityJitter | | glow | Glow | glowSpread, glowIntensity, nodeSize | | chaos | Chaos | nodeSpread (down), nodeSizeVar, speed, nodeDensityVar | | connect | Connect | connDist, lineOpacity, lineWidth, lineWidthVar | | weight | Weight | nodeSize, lineWidth, nodeOpacity, lineOpacity, hubEmphasis | | morph | Morph | nodeDensityVar, nodeSpawnRate, nodeFadeTime, breathSpawnSync | | depth | Depth | depthAmount, depthSpeedRatio (inverse) |


Visual Families (8)

The randomizer (reshuffle()) picks from 8 families, each committing to a distinct visual axis. Desktop selection is uniform across all families; mobile (viewport ≤ 768px) uses a weighted pick that favors dense families and downweights naturally sparse ones so phone rolls land on visually rich configs more often.

| Family | Mobile weight | Character | |--------|--------|-----------| | crystal-mesh | 2.5× | Dense, short connDist, near-static breath. Geometric lattice feel. | | clustered | 1.8× | Tight Gaussian clusters (low nodeSpread) with long inter-cluster connections. Negative length bias. | | variance-garden | 1.0× | Medium density, extreme size and width variation. Organic, heterogeneous look. | | motion-drift | 1.0× | Higher node speed, moderate density. Motion comes from speed, not curve oscillation. | | wave-line | 1.0× | Sinusoidal wave connections (lineStyle=3). Gentle organic ripple. | | asymmetric-duet | 0.5× | Two visually distinct networks — net 2 is 3–5× thicker than net 1, bowtie line style. | | sparse-depth | 0.4× | Few large glowing nodes with strong z-parallax. Slow drift, high curve softness. | | sparse-glow-line | 0.4× | Glow filament connections (lineStyle=4). Ambient node glow reduced so the lines carry the luminosity. |

After family sampling, universal pins (e.g., breathDistSync pinned to 0, breathIntensity pinned to 1.0) and clamps (e.g., speed capped at 0.1, glowIntensity capped at 0.25) enforce quality rules derived from blind grading. On mobile, additional floors apply (connDist ≥ 0.185, lineOpacity ≥ 0.7, nodeSize ≤ 1.6, nodeDensity ≥ 170) plus family-specific tweaks for sparse-glow-line and variance-garden.

Parameters never randomized: breathEnvelope, net1Color, net2Color, bgColor.


Wallpaper Rendering

When node density is high and connection distance is large, the animation automatically extends beyond the visible viewport via a "bleed ring" — extra nodes are spawned outside the canvas edges so connections don't abruptly clip at the border. This is not a mode you toggle; it's a first-class rendering feature that activates automatically for dense presets.

The bleed distance is computed by computeBleed():

bleed = smoothstep(100, 180, nodeDensity) * connDist * 1.2

Capped at min(viewportWidth, viewportHeight) * 0.35. On mobile, further capped to prevent excessive node counts. Node count is scaled by the bleed area so density remains consistent.


Performance

The animation is designed to run well everywhere:

  • Startup benchmark: Renders 500 nodes for 30 frames to measure device capability
  • Budget system: Benchmark result sets the density cap, connection budget, and glow cap
  • Runtime auto-scaling: Continuous frame-time monitoring adjusts budgets down if the device is struggling
  • Reduced motion: Detects OS prefers-reduced-motion preference. Renders single high-quality static frames on demand.
  • Safety brake: If FPS drops below 20 for 3 consecutive frames, auto-engages reduced motion and dispatches neural-canvas:low-fps with brakeEngaged: true. Released automatically on any reshuffle() / applyPreset() so a new preset always gets a fresh evaluation window. Disable via canvas.safetyBrake = false or the no-brake attribute.

Bundle size (dist/neural-canvas.min.js): ~107 KB raw / ~28 KB gzip. Single runtime dependency: seinx-design-tokens.


The Story

This animation was designed through convergent evaluation — a custom tool that generated random presets and let a human vote on them, narrowing the parameter space over ~340 evaluations across five rounds. The nine visual families emerged from patterns in what was consistently chosen. The algorithm learned taste from reactions.


v2.0.0 Breaking Changes

<neural-canvas> moved from light DOM to shadow DOM. This means:

  • The 8 CSS custom properties (--nc-color-net1, --nc-color-net2, etc.) are removed
  • The .refreshStyles() method is removed
  • External CSS selectors targeting internal canvases no longer match

Migration: Replace all CSS custom property usage with canvas.applyConfig({ ... }). See CHANGELOG.md for details.


Development

npm install
npm run build     # esbuild + tsc declarations
npm test          # vitest unit tests (235 tests)
npm run typecheck # tsc --noEmit

License

MIT


Built by SeinX