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

pvgview

v0.2.0

Published

Deterministic Procedural Vector Graphics (PVG 0.2) Engine with microsecond evaluation and <pvg-view> Web Component

Readme

⚡ pvgview NPM Package (PVG 0.2) — Overview & Complete Usage Guide

The pvgview npm package (v0.2.0) is the official, zero-dependency, isomorphic TypeScript & JavaScript engine for Procedural Vector Graphics (PVG 0.2) — full parity with the Rust pvg 0.2.0 core.

📜 Language specification: everything below is defined normatively in PVG_SPECS.md — start there to learn the language (Section 2 grammar, Sections 6-13 shapes & rendering, Section 18 host uniforms/noise/sprites/patterns/splines).

It brings the deterministic procedural graphics capabilities of the Rust engine directly to the JavaScript ecosystem—running seamlessly across Node.js, Deno, Bun, React, Vue, Svelte, Next.js, Nuxt, and Vanilla HTML.


🎯 What is the pvgview Package?

Traditional vector graphics formats like SVG are strictly declarative documents. To animate them or generate repetitive structures (e.g., radar rings, clock ticks, HUD meters, charts), developers must either bloat the SVG with duplicate XML tags or attach heavy JavaScript libraries that trigger browser DOM reflows and CSS recalculations.

pvgview solves this with a procedural-native approach:

  • Write 2D vector graphics with native for loops, variables, trigonometry (sin, cos), and timeline clocks (time).
  • Evaluates documents inside a < 50 KB heap budget with per-frame evaluation latencies of 5–40 µs.
  • Provides multiple output pipelines: HTML5 <canvas> (60+ FPS), W3C Static SVG, W3C SMIL-Animated SVG, and the drop-in <pvg-view> W3C Web Component.
  • PVG 0.2 parity: stroke topology (cap/join/miter/dash/align), gradient paints (linear/radial/angular + stop), blur/shadow/glow, clip masks, blend modes, functional rgb()/rgba() colors — plus host uniforms (param/PvgScene), deterministic noise2d/noise3d, pixel sprites (snap/filter), pattern tiles, splines (array/len/get), and for/while/if inside path bodies. Accepts both PVG 0.1 and PVG 0.2 headers (0.1 docs evaluate identically). Safety caps enforced per evaluation: 100k loop iterations, 64 call-stack frames, 50k draw commands.

🏗️ Architecture & Execution Pipeline

[ PVG 0.2 Source Code ]
           │
           ▼ (Lexer & Parser)
[ Cached Document AST ] (~8–16 KB) ───► Stored in memory once
           │
           ▼ (Evaluator at time t @ 60 FPS) ───► Runs in 5–30 µs
[ Flat 2D Draw List (`DrawList`) ] (~15–35 KB)
           │
   ┌───────┼──────────────────────────┬──────────────────────────┐
   ▼                                  ▼                          ▼
[ HTML5 2D Canvas ]          [ Static W3C SVG ]         [ SMIL Animated SVG ]
(High-DPI Retina Scaled)     (`toSvg()`)                (`toAnimatedSvg()`)

📊 Complete API Reference

| Export | Signature | Description | | :--- | :--- | :--- | | parse | (source: string) => Document | Tokenizes and parses PVG 0.2 source text into an Abstract Syntax Tree (Document). Accepts PVG 0.1 and PVG 0.2 headers. | | evaluate | (doc: Document, time?: number) => DrawList | Evaluates a pre-parsed AST at a specific timestamp ($t$). Skips re-parsing for 60 FPS animation loops. | | compile | (source: string, time?: number, params?: Record<string, Value>) => DrawList | Convenience single-pass function that parses and evaluates source text at timestamp ($t$), with optional host param overrides. | | compileWithParams | (source: string, params: Record<string, Value>, time?: number) => DrawList | Compiles with host uniforms (param, Section 18.1) overridden — mirrors pvg::compile_with_params. | | PvgScene | PvgScene.fromSource(src) / .fromDocument(doc) | Host-driven scene handle: parse once, setParam/clearParam/setTime per frame, evaluate(). Mirrors pvg::Scene. | | toSvg | (input: string \| DrawList, time?: number) => string | Serializes a PVG string or evaluated DrawList into standard W3C SVG XML (gradients, filters, clips, patterns, sprites, splines). | | toAnimatedSvg | (source: string, options?: AnimatedSvgOptions) => string | Compiles an animated PVG document into a standalone W3C SVG with SMIL animation tags. | | renderToCanvas | (ctx: CanvasRenderingContext2D, dl: DrawList, opts?: RenderCanvasOptions) => void | Rasterizes a DrawList directly onto an HTML5 2D Canvas context. | | PvgView | class PvgView extends HTMLElement | The <pvg-view> W3C Custom Element class. | | registerPvgView | (tagName?: string) => void | Manually registers the <pvg-view> custom element in the DOM (auto-registered in browsers). | | PvgColor | class PvgColor | 32-bit RGBA color representation with hex parsers (PvgColor.fromHex("#00ffcc")). | | Transform2D | class Transform2D | $2 \times 3$ Affine matrix transformation primitive. |


📜 Language Specification

The authoritative definition of the PVG language lives in PVG_SPECS.md (repo root). Use it as the reference for anything this guide summarizes:

  • Sections 2-4 — headers (PVG 0.1 / PVG 0.2), indentation, strings, colors (#rgb, keywords, rgb()/rgba()), expression precedence, built-ins (sin, random, noise2d, array/len/get, …)
  • Sections 6-7 — shapes (circle, ellipse, rectangle, line, polygon, text, path, spline, sprite) and the lean path commands
  • Sections 8-12 — 0.2 rendering: stroke topology, gradients, blur/shadow/glow, clip, blend
  • Sections 13-14 — group transforms, time/t animation clock
  • Section 15 — safety caps enforced by this package (100k loop iterations, 64 call-stack frames, 50k draw commands)
  • Section 18 — host uniforms (param), patterns, pixel mode, arrays & splines, control flow inside path

This package tracks the spec exactly: every valid 0.1 document evaluates identically under 0.2, and PVG 0.1 headers remain accepted (see the back-compatibility test in test/features_02.test.js).


💻 Practical Usage Patterns

1. Node.js / Server-Side Rendering (SSR) & SVG Export

Generate static SVGs on your server or in build scripts without headless browsers:

import { toSvg, compile } from "pvgview";
import fs from "node:fs";

const pvgSource = `
PVG 0.2
canvas 500 500
  background #0b0c10

# Concentric Range Rings
for r from 1 to 4
  circle
    center [250, 250]
    radius r * 50
    fill none
    stroke #103b42
    width 1.5

# Central Core
circle
  center [250, 250]
  radius 10
  fill #00ffcc
`;

// 1. Direct SVG generation
const svgXml = toSvg(pvgSource);
fs.writeFileSync("radar.svg", svgXml, "utf-8");

// 2. Inspect evaluated geometric primitives
const drawList = compile(pvgSource);
console.log(`Canvas: ${drawList.canvasWidth}x${drawList.canvasHeight}`);
console.log(`Rendered Shapes: ${drawList.items.length}`);

2. High-Performance 60 FPS Canvas (Two-Phase AST Caching)

In browser applications, avoid string re-parsing churn by compiling the AST once and evaluating only the AST on every frame tick:

import { parse, evaluate, renderToCanvas } from "pvgview";

const canvas = document.querySelector<HTMLCanvasElement>("#viewport")!;
const ctx = canvas.getContext("2d")!;

const pvgSource = `
PVG 0.2
canvas 400 400
  background #080a0f

set cx = 200
set cy = 200
set pulse = 50 + 20 * sin(time * 4.0)

circle
  center [cx, cy]
  radius pulse
  fill #ff0055
  stroke #ffffff
  width 2.0

circle
  center [cx + 100 * cos(time * 2.0), cy + 100 * sin(time * 2.0)]
  radius 10
  fill #00ffcc
`;

// Phase 1: Parse string to AST once
const ast = parse(pvgSource);

// Phase 2: Microsecond evaluation loop (~15–30 µs per frame)
function renderFrame(timeMs: number) {
  const t = timeMs / 1000.0;
  
  // Evaluate AST at timestamp t
  const drawList = evaluate(ast, t);

  // High-DPI Retina Canvas Scaling
  const dpr = window.devicePixelRatio || 1;
  const targetW = canvas.clientWidth * dpr;
  const targetH = canvas.clientHeight * dpr;

  if (canvas.width !== targetW || canvas.height !== targetH) {
    canvas.width = targetW;
    canvas.height = targetH;
  }

  // Calculate proportional zoom & letterbox alignment
  const zoom = Math.min(targetW / drawList.canvasWidth, targetH / drawList.canvasHeight);
  const originX = (targetW - drawList.canvasWidth * zoom) / 2;
  const originY = (targetH - drawList.canvasHeight * zoom) / 2;

  // In-place Canvas render
  renderToCanvas(ctx, drawList, { originX, originY, zoom });

  requestAnimationFrame(renderFrame);
}

requestAnimationFrame(renderFrame);

3. Generating SMIL-Animated SVGs

Create self-contained animated vector files that loop inside standard <img> tags without JavaScript:

import { toAnimatedSvg } from "pvgview";
import fs from "node:fs";

const pulsingRadar = `
PVG 0.2
canvas 400 400
  background #000000

set sweep = time * 3.14159

line
  from [200, 200]
  to   [200 + 150 * cos(sweep), 200 + 150 * sin(sweep)]
  stroke #00ffcc
  width 2.0
`;

// Exports SVG with W3C <animate> SMIL keyframe tags
const animatedSvg = toAnimatedSvg(pulsingRadar, {
  duration: 2.0, // 2-second loop
  fps: 30        // 30 frames per second
});

fs.writeFileSync("radar_animated.svg", animatedSvg, "utf-8");

4. <pvg-view> Drop-In Web Component

The <pvg-view> custom element allows zero-boilerplate vector rendering in plain HTML, Markdown, or web apps:

<!-- 1. Import PVG -->
<script type="module">
  import "pvgview";
</script>

<!-- 2. Use the Custom Element -->
<pvg-view autoplay interactive render="canvas" style="width: 400px; height: 400px;">
  <script type="text/pvg">
    PVG 0.2
    canvas 400 400
      background #080a0f

    circle
      center [200, 200]
      radius 60 + 20 * sin(time * 3.0)
      fill #00ffcc
  </script>
</pvg-view>

<pvg-view> Attributes:

  • autoplay: Automatically runs the 60 FPS timeline loop.
  • interactive: Enables mouse dragging (pan) and scroll-wheel (zoom).
  • render="canvas" / render="svg": Selects GPU-accelerated Canvas or DOM SVG backend.
  • code="...": Dynamically binds a PVG code string.
  • src="path/to/file.pvg": Fetches and executes remote .pvg files.
  • fps="60": Caps animation frame rate.
  • time="1.25": Manually scrubs the timeline clock.

5. React Integration Pattern

Wrap pvgview in a lightweight, reusable React component:

import React, { useEffect, useRef } from "react";
import { parse, evaluate, renderToCanvas } from "pvgview";

interface PvgCanvasProps {
  code: string;
  className?: string;
  style?: React.CSSProperties;
}

export const PvgCanvas: React.FC<PvgCanvasProps> = ({ code, className, style }) => {
  const canvasRef = useRef<HTMLCanvasElement | null>(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;

    let animId: number;
    let ast: ReturnType<typeof parse> | null = null;

    try {
      ast = parse(code);
    } catch (e) {
      console.error("PVG Syntax Error:", e);
      return;
    }

    const t0 = performance.now();

    const loop = (now: number) => {
      if (!ast) return;
      const t = (now - t0) / 1000.0;
      const drawList = evaluate(ast, t);

      const dpr = window.devicePixelRatio || 1;
      const w = canvas.clientWidth * dpr;
      const h = canvas.clientHeight * dpr;

      if (canvas.width !== w || canvas.height !== h) {
        canvas.width = w;
        canvas.height = h;
      }

      const zoom = Math.min(w / drawList.canvasWidth, h / drawList.canvasHeight);
      const originX = (w - drawList.canvasWidth * zoom) / 2;
      const originY = (h - drawList.canvasHeight * zoom) / 2;

      renderToCanvas(ctx, drawList, { originX, originY, zoom });
      animId = requestAnimationFrame(loop);
    };

    animId = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(animId);
  }, [code]);

  return <canvas ref={canvasRef} className={className} style={{ width: "100%", height: "100%", ...style }} />;
};

6. PVG 0.2 Features (gradients, clip, host params)

import { compile, PvgScene } from "pvgview";

const src = `
PVG 0.2
canvas 512 512
  background #07090e

param hull_hp: 0.65

rectangle
  pos [106, 106]
  size [300, 300]
  radius 36
  fill linear [106, 106] [406, 406]
    stop 0.0 #2b3040
    stop 1.0 #0e1017
  shadow [0, 18] 24 #000000bb

clip
  circle
    center [256, 256]
    radius 105
  circle
    center [256, 256]
    radius 90
    fill radial [256, 256] 90
      stop 0.0 #00ffff
      stop 1.0 #07090e
`;

// One-shot with overrides:
const dl = compile(src, 1.0, { hull_hp: 0.2 });

// Or host-driven (parse once, evaluate per frame):
const scene = PvgScene.fromSource(src);
scene.setParam("hull_hp", 0.2);
scene.setTime(1 / 60);
const frame = scene.evaluate();

Back-compat: PVG 0.1 headers are still accepted and evaluate identically (new style props default to butt/miter/solid/normal, no filters, no clipping).


📦 Package Distribution Structure

When consumers install pvgview from npm (npm install pvgview), the package exports:

node_modules/pvgview/
├── dist/
│   ├── index.js          # ESM entry point (import { parse } from 'pvgview')
│   ├── index.cjs         # CommonJS entry point (const { parse } = require('pvgview'))
│   ├── index.d.ts        # TypeScript declarations
│   ├── index.global.js   # Browser bundle for CDNs (window.PVG)
│   ├── component.js      # Isolated <pvg-view> submodule
│   └── component.d.ts    # Web Component TypeScript declarations
├── package.json
└── README.md

This ensures compatibility across all major bundlers (Vite, Webpack, Rollup, esbuild, Turbopack) and runtime environments.