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

@hypertools/sdk

v0.4.6

Published

Vanilla-first SDK for embedding interactive creative coding experiences. Supports p5.js, Three.js, WebGL, and more.

Readme

@hypertools/sdk

Vanilla-first SDK for embedding interactive creative coding experiences. Works with p5.js, Three.js, WebGL, Canvas, and any other rendering library.

npm version npm downloads License: MIT

Features

  • Framework-agnostic - Works with p5.js, Three.js, vanilla Canvas, WebGL, or any rendering library
  • Reactive parameters - Define control parameters that automatically trigger updates
  • Video & image capture - Built-in recording with MediaRecorder and canvas capture
  • Timeline animations - Keyframe-based animation system with easing functions
  • React integration - Optional React hooks and components
  • TypeScript first - Full type definitions included
  • Zero dependencies - Core SDK has no runtime dependencies

Installation

# npm
npm install @hypertools/sdk

# yarn
yarn add @hypertools/sdk

# pnpm
pnpm add @hypertools/sdk

# bun
bun add @hypertools/sdk

Quick Start

Basic Usage

import { Experience } from '@hypertools/sdk';

const experience = new Experience({
  mount: document.getElementById('canvas-container'),
  paramDefs: {
    color: { type: 'color', value: '#ff0000', label: 'Color' },
    speed: { type: 'number', value: 5, min: 1, max: 10, label: 'Speed' },
    showGrid: { type: 'boolean', value: true, label: 'Show Grid' },
  },
  setup(context) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    canvas.width = 800;
    canvas.height = 600;
    context.mount.appendChild(canvas);

    // Access reactive parameters
    console.log(context.params.color);   // '#ff0000'
    console.log(context.params.speed);   // 5
    console.log(context.params.showGrid); // true

    // Draw loop
    function draw() {
      ctx.fillStyle = context.params.color;
      ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    // Listen to frame events
    context.experience.on('frame', draw);

    // Return cleanup function
    return () => {
      canvas.remove();
    };
  },
});

// Control playback
experience.play();
experience.pause();

// Update parameters programmatically
experience.setParam('color', '#00ff00');
experience.setParams({ speed: 8, showGrid: false });

// Capture images
const blob = await experience.captureImage('png');

// Clean up when done
experience.destroy();

With p5.js

import { Experience } from '@hypertools/sdk';
import p5 from 'p5';

new Experience({
  mount: document.getElementById('sketch'),
  paramDefs: {
    backgroundColor: { type: 'color', value: '#1a1a2e' },
    circleCount: { type: 'number', value: 10, min: 1, max: 50 },
    animate: { type: 'boolean', value: true },
  },
  setup(context) {
    let sketch;

    new p5((p) => {
      sketch = p;

      p.setup = () => {
        p.createCanvas(800, 600);
      };

      p.draw = () => {
        if (!context.params.animate) return;

        p.background(context.params.backgroundColor);

        for (let i = 0; i < context.params.circleCount; i++) {
          p.circle(
            p.random(p.width),
            p.random(p.height),
            p.random(20, 50)
          );
        }
      };
    }, context.mount);

    return () => sketch?.remove();
  },
});

With Three.js

import { Experience } from '@hypertools/sdk';
import * as THREE from 'three';

new Experience({
  mount: document.getElementById('scene'),
  paramDefs: {
    rotationSpeed: { type: 'number', value: 0.01, min: 0, max: 0.1, step: 0.001 },
    cubeColor: { type: 'color', value: '#00ff88' },
    wireframe: { type: 'boolean', value: false },
  },
  setup(context) {
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(75, 800 / 600, 0.1, 1000);
    const renderer = new THREE.WebGLRenderer();

    renderer.setSize(800, 600);
    context.mount.appendChild(renderer.domElement);

    const geometry = new THREE.BoxGeometry();
    const material = new THREE.MeshBasicMaterial({
      color: context.params.cubeColor,
      wireframe: context.params.wireframe,
    });
    const cube = new THREE.Mesh(geometry, material);
    scene.add(cube);

    camera.position.z = 5;

    // Register object for external access
    context.registerObject('mainCube', cube);

    // Animation loop
    context.experience.on('frame', () => {
      cube.rotation.x += context.params.rotationSpeed;
      cube.rotation.y += context.params.rotationSpeed;
      material.color.set(context.params.cubeColor);
      material.wireframe = context.params.wireframe;
      renderer.render(scene, camera);
    });

    return () => {
      renderer.dispose();
      geometry.dispose();
      material.dispose();
    };
  },
});

Entry Points

The SDK is modular - import only what you need:

| Entry | Purpose | |-------|---------| | @hypertools/sdk | Core Experience class + utilities | | @hypertools/sdk/controls | Tweakpane-based control panel (requires tweakpane peer dep) | | @hypertools/sdk/recording | Video/image capture & timeline | | @hypertools/sdk/react | React hooks & components (requires react peer dep) | | @hypertools/sdk/capture | Low-level capture utilities |

API Reference

Experience

The main class for creating interactive experiences.

interface ExperienceConfig {
  mount: HTMLElement;              // Container element
  paramDefs?: ParamDefinitions;    // Parameter schema
  initialParams?: Record<string, unknown>;  // Override defaults
  setup: SetupFunction;            // Your code
  autoplay?: boolean;              // Start immediately (default: true)
  frameRate?: number;              // Target FPS (default: 60)
  background?: string;             // CSS background
}

Methods

// Playback
experience.play();
experience.pause();
experience.toggle();

// Parameters
experience.setParam('key', value);
experience.setParams({ key1: value1, key2: value2 });
experience.getParams();  // Get all current values
experience.resetParams(); // Reset to defaults

// Object registry (Spline-like API)
experience.registerObject('name', object, metadata);
experience.findObjectByName('name');
experience.findObjectById('id');
experience.getAllObjects();

// Events
experience.on('ready', () => {});
experience.on('frame', ({ frame, deltaTime }) => {});
experience.on('paramChange', ({ key, value, previousValue }) => {});
experience.on('resize', ({ width, height }) => {});
experience.on('error', ({ error }) => {});
experience.once('ready', () => {});
experience.off('ready', handler);

// Capture
const blob = await experience.captureImage('png');

// Cleanup
experience.destroy();

Properties

experience.isReady;      // boolean
experience.isPlaying;    // boolean
experience.isDestroyed;  // boolean
experience.currentFrame; // number
experience.params;       // Reactive proxy
experience.mount;        // HTMLElement

Parameter Types

// Number
{ type: 'number', value: 5, min: 0, max: 10, step: 0.1, label: 'Speed' }

// Color
{ type: 'color', value: '#ff0000', label: 'Background' }

// Boolean
{ type: 'boolean', value: true, label: 'Show Grid' }

// String
{ type: 'string', value: 'hello', label: 'Text' }

// Select
{ type: 'select', value: 'option1', options: [
  { label: 'Option 1', value: 'option1' },
  { label: 'Option 2', value: 'option2' },
], label: 'Mode' }

Recording Module

import { VideoRecorder, ImageCapture, Timeline } from '@hypertools/sdk/recording';

// Video recording
const recorder = VideoRecorder.start(canvas, {
  format: 'webm',      // 'webm' | 'mp4'
  quality: 0.9,        // 0-1
  frameRate: 60,       // FPS
});

// Later...
const videoBlob = await recorder.stop();

// Image capture
const imageBlob = await ImageCapture.capture(canvas, 'png');

// Timeline animation
const timeline = new Timeline({
  loop: true,
  duration: 5000,  // 5 seconds
});

timeline
  .addKeyframe(0, { opacity: 0, scale: 0.5 })
  .addKeyframe(1000, { opacity: 1, scale: 1 }, 'easeOutBack')
  .addKeyframe(4000, { opacity: 1, scale: 1 })
  .addKeyframe(5000, { opacity: 0, scale: 0.5 }, 'easeInQuad');

timeline.onUpdate((params) => {
  experience.setParams(params);
});

timeline.play();
timeline.pause();
timeline.seek(2500);  // Jump to 2.5s

React Integration

import { useExperience, ExperienceView } from '@hypertools/sdk/react';

function MyVisualization() {
  const {
    experience,
    isReady,
    isPlaying,
    params,
    setParam,
    play,
    pause,
    toggle,
  } = useExperience({
    paramDefs: {
      color: { type: 'color', value: '#ff0000' },
      speed: { type: 'number', value: 5, min: 1, max: 10 },
    },
    setup(context) {
      // Your setup code
    },
  });

  return (
    <div>
      <ExperienceView
        experience={experience}
        className="w-full h-96"
      />

      <div className="controls">
        <button onClick={toggle}>
          {isPlaying ? 'Pause' : 'Play'}
        </button>

        <input
          type="color"
          value={params.color}
          onChange={(e) => setParam('color', e.target.value)}
        />

        <input
          type="range"
          min={1}
          max={10}
          value={params.speed}
          onChange={(e) => setParam('speed', Number(e.target.value))}
        />
      </div>
    </div>
  );
}

Controls Module (Tweakpane)

Add a visual control panel with Tweakpane:

import { HypertoolControls } from '@hypertools/sdk/controls';

const controls = new HypertoolControls({
  container: document.getElementById('controls'),
  definitions: {
    color: { type: 'color', value: '#ff0000' },
    speed: { type: 'number', value: 5, min: 1, max: 10 },
  },
  onChange: (key, value) => {
    console.log(`${key} changed to ${value}`);
  },
});

// Programmatic updates
controls.set('color', '#00ff00');
controls.refresh();
controls.dispose();

Using Exported HyperTools Experiences

When you export a project from HyperTools, you get a standalone web component that can be embedded anywhere. Use ExperienceController from this SDK to control it programmatically.

Setup

  1. Get your exported experience - Export from HyperTools to get a JS file (e.g., my-experience.js)

  2. Load the experience in your HTML:

<script src="./my-experience.js"></script>
<my-experience></my-experience>
  1. Install the SDK to control it:
npm install @hypertools/sdk

Basic Control

import { ExperienceController } from '@hypertools/sdk';

// Get reference to the web component
const element = document.querySelector('my-experience');

// Connect the controller
const controller = new ExperienceController({
  element,
  initialParams: {
    speed: 5,
    color: '#ff0000',
  },
});

// Control parameters
controller.setParam('speed', 10);
controller.setParams({ speed: 8, color: '#00ff00' });

// Get current values
const params = controller.getParams();
const paramDefs = controller.getParamDefs();

// Reset to defaults
controller.resetParams();

// Listen to changes
controller.on('paramChange', (event) => {
  console.log(`${event.key} changed to ${event.value}`);
});

// Cleanup when done
controller.destroy();

Dispatching Events

Trigger interactions programmatically by dispatching synthetic events:

// Simulate a click
controller.dispatchToCanvas('click', { clientX: 400, clientY: 300 });

// Simulate a long press (e.g., for formation triggers)
controller.dispatchToCanvas('mousedown', { clientX: 400, clientY: 300 });
setTimeout(() => {
  controller.dispatchToCanvas('mouseup', { clientX: 400, clientY: 300 });
}, 600);

// Keyboard events
controller.dispatchToCanvas('keydown', { key: 'Space' });

// Custom events
controller.dispatchCustomEvent('myEvent', { data: 'hello' });

React Integration

import { useEffect, useRef, useState } from 'react';
import { ExperienceController } from '@hypertools/sdk';
import type { ExportedExperienceElement } from '@hypertools/sdk';

function App() {
  const experienceRef = useRef<ExportedExperienceElement>(null);
  const controllerRef = useRef<ExperienceController | null>(null);
  const [isReady, setIsReady] = useState(false);

  useEffect(() => {
    const element = experienceRef.current;
    if (!element) return;

    const onReady = () => {
      controllerRef.current = new ExperienceController({ element });
      setIsReady(true);
    };

    element.addEventListener('ready', onReady, { once: true });

    return () => {
      element.removeEventListener('ready', onReady);
      controllerRef.current?.destroy();
    };
  }, []);

  return (
    <div>
      {/* @ts-expect-error - Custom element */}
      <my-experience ref={experienceRef} />

      {isReady && (
        <button onClick={() => controllerRef.current?.setParam('speed', 10)}>
          Speed Up
        </button>
      )}
    </div>
  );
}

ExperienceController API

// Constructor options
interface ExperienceControllerConfig {
  element: ExportedExperienceElement;  // The web component
  initialParams?: Record<string, unknown>;  // Override initial values
  autoConnect?: boolean;  // Connect immediately (default: true)
}

// Methods
controller.connect();      // Connect to element
controller.disconnect();   // Disconnect from element
controller.destroy();      // Full cleanup

controller.setParam(key, value);
controller.setParams(params);
controller.getParams();
controller.getParamDefs();
controller.resetParams();

controller.on(event, handler);   // Subscribe
controller.once(event, handler); // Subscribe once
controller.off(event, handler);  // Unsubscribe

controller.dispatchToCanvas(eventType, eventInit);  // Dispatch to canvas
controller.dispatchToElement(eventType, eventInit); // Dispatch to element
controller.dispatchCustomEvent(type, detail);       // Custom event

controller.getCanvas();  // Get canvas element
controller.getMount();   // Get mount element

// Properties
controller.element;      // The web component
controller.isConnected;  // Connection status
controller.isDestroyed;  // Destroyed status

// Static factories
ExperienceController.fromSelector('my-experience');
await ExperienceController.whenDefined('my-experience');

Building Custom Features

See examples/react-landing/ for a complete example showing how to build custom features on top of exported experiences:

  • Preset System - Pre-configured settings with visual selection
  • Click-to-Form Mode - Click anywhere to trigger interactions at that position
  • Auto-pilot Mode - Automatically cycle through presets
  • Idle Screensaver - Start animations after inactivity
  • Share Configuration - Generate shareable URLs with encoded settings
  • Keyboard Shortcuts - Custom keyboard controls

Examples

See the /examples directory for complete working examples:

  • examples/react-landing/ - React app with exported experience as background + custom features
  • examples/vanilla-canvas/ - Pure Canvas API
  • examples/p5js/ - p5.js sketch
  • examples/threejs/ - Three.js scene
  • examples/react/ - React integration
  • examples/recording/ - Video capture & timeline

Browser Support

  • Chrome 80+
  • Firefox 78+
  • Safari 14+
  • Edge 80+

TypeScript

Full TypeScript support with type definitions included.

import type {
  Experience,
  ExperienceConfig,
  ExperienceContext,
  ParamDefinitions,
  ParamValues,
  ExperienceEvent,
} from '@hypertools/sdk';

Contributing

Contributions are welcome! Please read our Contributing Guide for details.

License

MIT License - see LICENSE for details.