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

@slithy/prim-lib

v0.3.2

Published

Core engine for primitive-based image reconstruction.

Readme

@slithy/prim-lib

Core engine for primitive-based image reconstruction. Consumed by @slithy/prim-interface.

What it does

Reconstructs an image by iteratively placing geometric shapes. Each step evaluates candidate shapes, mutates them, picks the one that most reduces the difference from the target, and draws it onto the working canvas.

Exports

Classes

  • Canvas — wraps HTMLCanvasElement; handles image loading, pixel reads, drawing steps, and SVG output
  • Optimizer — runs the rAF loop; calls onStep after each shape is placed
  • State — holds the current canvas and its distance from the target
  • Step — a single candidate shape placement; computes best color and difference change

Shapes

  • Shape — abstract base
  • Triangle, Rectangle, Ellipse, Square, Hexagon, Glyph, Debug

Types

Cfg — runtime config (all fields required):

interface Cfg {
  width: number         // image width for computation (set by Canvas.original)
  height: number        // image height for computation (set by Canvas.original)
  steps: number         // total shapes to place
  shapes: number        // candidate shapes evaluated per step
  mutations: number     // hill-climb attempts per candidate
  alpha: number         // starting shape opacity
  mutateAlpha: boolean  // whether opacity is mutated per candidate
  computeSize: number   // max image dimension for pixel distance calculations
  viewSize: number      // max image dimension for display canvas
  allowUpscale?: boolean // allow output larger than source image (default: false)
  scale?: number        // viewSize / computeSize ratio (set by Canvas.original)
  shapeTypes: Array<new (w: number, h: number) => ShapeInterface>
  fill: 'auto' | string
}
  • PreCfgCfg with optional width/height; used before image dimensions are known
  • ShapeInterface — structural interface for shapes; includes toData(alpha, color): StepData
  • Bbox, Point, ImageDataLike, ShapeImageData

RGB[number, number, number] tuple

StepData — discriminated union of serialized shape data, keyed by t:

type StepData =
  | { t: 't';  a: number; c: RGB; pts: [number, number][] }       // Triangle
  | { t: 'r';  a: number; c: RGB; pts: [number, number][] }       // Rectangle
  | { t: 'e';  a: number; c: RGB; cx: number; cy: number; rx: number; ry: number } // Ellipse
  | { t: 's';  a: number; c: RGB; cx: number; cy: number; r: number }              // Square
  | { t: 'h';  a: number; c: RGB; cx: number; cy: number; r: number; angle: number } // Hexagon
  | { t: 'sm'; a: number; c: RGB; cx: number; cy: number; fs: number; text: string } // Glyph

SerializedOutput — compact, storage-ready representation of a completed run:

interface SerializedOutput {
  v: 1             // schema version
  w: number        // compute width
  h: number        // compute height
  scale: number    // viewSize / computeSize ratio
  fill: RGB        // background fill color
  steps: StepData[]
}

ReplayResult — returned by replayOutput:

interface ReplayResult {
  raster: HTMLCanvasElement
  svg: SVGSVGElement
  svgString: string
}

Functions

replayOutput(data: SerializedOutput): ReplayResult — reconstructs canvas and SVG natively from serialized output, without re-running the optimizer. Useful for restoring saved results from localStorage or a database.

Utilities

  • getFill(data: ImageDataLike): string — computes a fill color (average of corner pixels) from image data
  • parseColor(str: string): RGB — parses "rgb(r, g, b)" to an [r, g, b] tuple
  • stepPerf — accumulator for profiling rasterize vs pixel math time per step

Differences from primitive.js

prim-lib is derived from primitive.js, a JavaScript port of the Go primitive library by Michael Fogleman. The core algorithm — iterative shape placement via hill-climbing — is unchanged. What's different:

  • TypeScript, strict mode — fully typed throughout; strict: true on both packages
  • ESM only — no CommonJS; no importScripts()
  • Canvas reuse — the original creates a new <canvas> element for every shape rasterization call (~58,000 per run at default settings). prim-lib reuses a single module-level canvas, growing it only when a larger bbox is seen. This delivered a ~27× speedup (37 s → ~0.8 s rasterize time per run)
  • willReadFrequently: true — canvas contexts used for getImageData are created with this flag, keeping pixel data in CPU memory and suppressing browser warnings
  • Workers removed — the original included worker stubs (importScripts()-based, not ESM-compatible). Workers were investigated and benchmarked; after the canvas reuse optimization, a batched worker implementation matched single-threaded performance. Workers are not used
  • Architecture split — the original is a single-layer library. Here, prim-lib is the pure algorithm (no knowledge of how images arrive or where results go), and prim-interface is the adapter that wires it to the browser
  • PreCfg / Cfg distinctionCanvas.original() accepts PreCfg (optional width/height) and resolves to a fully-populated Cfg, making the config lifecycle explicit in the types
  • SerializationStepData / SerializedOutput allow completed runs to be stored compactly and replayed via replayOutput() without re-running the optimizer

Architecture notes

  • Shape.rasterize() reuses a single module-level canvas (_rasterCanvas) grown to the max shape size seen; avoids per-call createElement('canvas') which was the dominant cost (27x speedup)
  • Cfg.shapeTypes holds shape constructors; shapes are chosen randomly each step
  • PreCfg exists to bridge the gap between call time (dimensions unknown) and runtime (dimensions set by Canvas.original())
  • Canvas.svgRoot() is a static helper shared by Canvas.empty() and replayOutput() to create the SVG root with clip path and background fill