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

telecanvas

v0.1.0

Published

Shared remote SVG canvas: draw in real physical units (mm) on any networked display

Readme

TeleCanvas

Shared remote SVG canvas: draw in real physical units (mm) on any networked display.

Run a tiny host server, open its URL on a TV / projector / any browser, and push SVG markup to it over HTTP. Every connected viewer mirrors the canvas live. After a one-minute on-screen calibration, one SVG unit is exactly one millimeter on the physical display — an 80-unit-wide marker is 80 mm wide on glass.

Quick start

npx telecanvas
# [INFO] TeleCanvas listening at http://localhost:8100/
# [INFO] TeleCanvas listening at http://192.168.1.23:8100/

Open the URL on the display, then draw from anywhere:

curl -X PUT --data-raw '<circle r="40" fill="red"/>' http://192.168.1.23:8100/

Coordinate contract

The viewer computes its SVG viewBox so that:

  • 1 SVG unit = 1 mm on the physical display (once calibrated),
  • the origin is at the center of the viewport,
  • y grows downward (standard SVG orientation).

A 1080p TV that is 940 mm wide shows roughly x ∈ [-470, 470]. Content is not scaled to fit — it is drawn physically true, which is the point.

Wire contract

PUT / with the canvas' inner SVG markup as the raw text body. The server stores it as the current canvas, answers 200, and broadcasts it to every connected viewer. CORS is permissive (Access-Control-Allow-Origin: *, PUT allowed), so browsers can push cross-origin. Each PUT replaces the whole canvas; push the full document every time (coalescing rapid updates is the client's job — see TeleCanvasClient).

Viewers subscribe via WebSocket on the same port and receive each update as one line-framed JSON string. This viewer transport is package-internal and may change; the PUT / contract is stable.

CLI

Usage: telecanvas [options]

  -p, --port <n>       Port to listen on (default: 8100)
      --host <addr>    Address to bind (default: all interfaces)
      --persist <file> File persisting canvas content across restarts
                       (default: telecanvas-buffer.txt in the working directory)
      --no-persist     Disable persistence
      --kiosk          Also open the viewer fullscreen in a kiosk browser
  -h, --help           Show this help
  -v, --version        Print version

--kiosk launches an installed Chromium-family browser (Chrome, Chromium, Edge, Brave) in chromeless fullscreen with a throwaway profile, and closes it together with the server on Ctrl-C. Without one installed, the default browser opens as a regular window — press F11 yourself.

Library API

import { createServer, push, TeleCanvasClient } from 'telecanvas';

// Host a canvas programmatically
const server = await createServer({ port: 8100, persist: 'canvas.txt' });
console.log(server.urls); // reachable viewer URLs
server.push('<rect x="-40" y="-25" width="80" height="50" fill="cyan"/>');
console.log(server.content); // current canvas markup
await server.close(); // flushes persistence, disconnects viewers

// One-shot push to any TeleCanvas server (Node >= 18 or browser)
await push('http://192.168.1.23:8100/', '<circle r="40"/>');

// Continuous pusher: coalesces rapid updates so at most one PUT is in
// flight and only the newest frame is sent next — safe to call per
// animation frame
const client = new TeleCanvasClient('http://192.168.1.23:8100/');
client.push('<circle r="41"/>');
await client.clear();

createServer options: port (default 8100, 0 for ephemeral), host, persist (file path; omit to disable), staticDir (serve your own viewer directory instead of the built-in page).

The viewer page is embedded in the server bundle itself, so the package stays self-contained even when another app bundles it (vite, rollup, esbuild, Electron) — no static files need to survive the build.

subscribe(url, onUpdate) taps the live viewer feed (browser or Node ≥ 22): onUpdate receives the current canvas markup on connect and on every update, with automatic reconnection until the returned dispose function is called.

Embedding a viewer in an app

mountView attaches a live, view-only viewer to any element — no framework dependency:

import { mountView } from 'telecanvas';

// Fills its container; CSS px map to mm at the 96 dpi reference
const view = mountView(container, { src: 'http://192.168.1.23:8100/' });

// Emulate a fixed-size external display (mm). One dimension given, the
// other follows the container's aspect ratio; both given, the emulated
// display keeps its aspect and letterboxes inside the container
view.update({ width: 940, height: 529 });

view.unmount();

The injected <svg> follows the mm-centered coordinate contract and is owned entirely by mountView, so the container may live inside a framework-managed DOM tree — frameworks never reconcile children they didn't render. Keep the container a dedicated leaf (no template children on it). In Vue:

<script setup>
import { mountView } from 'telecanvas';
const el = ref();
let view;
onMounted(() => (view = mountView(el.value, { src, width: 940 })));
onBeforeUnmount(() => view.unmount());
</script>

<template><div ref="el" class="preview" /></template>

Scale calibration

Browsers guess the display's physical size and are usually wrong on TVs and projectors, so the viewer shows an “Uncalibrated” hint until you calibrate. Open the calibration overlay with the 📏 button in the corner, the c key, or by loading the page with ?calibrate. Three methods:

  1. Ruler (most accurate) — the overlay renders a bar of known pixel width; measure it with a ruler or tape, type the length in mm.
  2. Credit card — hold any ISO ID-1 card (85.60 × 53.98 mm) against the screen and drag the outline until it matches. No tools needed.
  3. Diagonal — type the advertised panel diagonal in inches. Least accurate; fine as a starting estimate.

Each method shows the computed px/mm and the implied display dimensions as immediate sanity feedback. Saving stores the scale in localStorage (per-origin, per-device). The historical devtools interface still works as an escape hatch: window.scale = 4.5 (px per mm), assign garbage to clear.

Development

npm install
npm test       # vitest
npm run build  # typecheck + rollup → dist/
node dist/cli.js

License

MIT