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

@flare-engine/shapes

v0.4.6

Published

Procedural 2D shape primitives for Skia — atomic primitives + SVG path escape hatch, composable for fast game prototyping

Readme

@flare-engine/shapes

Procedural 2D shape primitives for react-native-skia. 12 named atomic shapes + an SVG path escape hatch — designed for fast 2D game prototyping where you compose entities from primitives instead of importing sprite art.

Why

Mobile and indie game prototypes don't need sprite sheets to start. Compose ships, enemies, pickups, and UI shapes from primitives, ship a playable build, and replace shapes with art only after the mechanics lock — exactly the workflow Unity Shapes2D and Defold prototyping threads document. Shape-first prototyping keeps the iteration loop tight: a project can reach a playable state immediately and swap in sprite art only after mechanics lock.

Install

bun add @flare-engine/shapes @shopify/react-native-skia

@shopify/react-native-skia is an optional, type-only peer — src/draw.ts imports it via import type only (for the injected Skia / canvas / paint handles), so it never enters the runtime bundle. Skip it entirely on web/CanvasKit; add it only where you actually render on native Skia.

Usage

import type { Skia as SkiaModule, SkCanvas, SkPaint } from '@shopify/react-native-skia';
import { drawShape, type ShapeSpec } from '@flare-engine/shapes';

// `Skia` from react-native-skia is a value; the handle type is `typeof Skia`
// (mirrors `type Skia = typeof SkiaModule` inside src/draw.ts).
type Skia = typeof SkiaModule;

function demo(skia: Skia, canvas: SkCanvas, paint: SkPaint): void {
  // Compose any entity from primitives.
  const shape: ShapeSpec = { kind: 'circle', r: 24, fill: 0xff8844cc };
  drawShape(skia, canvas, paint, shape, /* x */ 100, /* y */ 200, /* rot */ 0);

  // Need a custom shape? Use the SVG escape hatch:
  const star: ShapeSpec = {
    kind: 'path',
    d: 'M 128 0 L 168 80 L 256 93 L 192 155 L 207 244 L 128 202 L 49 244 L 64 155 L 0 93 L 88 80 Z',
    boundsR: 256,
    fill: 0xffc9a24a,
  };
  drawShape(skia, canvas, paint, star, 0, 0, 0);
}

Primitives

13 spec kinds covering every common 2D shape. All accept optional fill (ARGB int), stroke, strokeWidth, ox/oy (local offset), rot (radians), and glow: 'friendly' | 'hazard'.

| Kind | Fields | Use case | |---|---|---| | circle | r | Bullets, pickups, eyes | | rect | w, h | Weapons, HUD bars, frames | | triangle | size | Ship hulls, arrows, sigils | | ngon | n, r | Hex/pentagon/octagon hulls | | star | points, rOuter, rInner | Sigils, sparks, pickups | | ring | r | Auras, target markers | | polyline | points: number[] | Bolts, freehand strokes | | gradient-rect | w, h, colorA, colorB, axis: 0\|1 | HP bars, sky strips | | arc | r, startAngle, endAngle, sector? | Half-circle, 1/4-moon, 1/8-moon | | annulus | rOuter, rInner, startAngle?, endAngle? | Donut, half-donut, partial rings | | trapezoid | topW, botW, h | Ship cockpits, pedestals | | path | d, boundsR | Escape hatch — any SVG path string | | ship | hull, weapons[], engine | Composite (3-element rule) |

Composing entities

import type { ShapeSpec } from '@flare-engine/shapes';

// A ship: hull + 2 weapons + engine, painted back-to-front.
const playerShip: ShapeSpec = {
  kind: 'ship',
  hull: { kind: 'triangle', size: 28, fill: 0xffc9a24a },
  weapons: [
    { kind: 'rect', w: 6, h: 12, fill: 0xffc9a24a, ox: -14, oy: 8 },
    { kind: 'rect', w: 6, h: 12, fill: 0xffc9a24a, ox: 14, oy: 8 },
  ],
  engine: { kind: 'square', size: 20, fill: 0xff806020 },
};

// A pickup: ring + inner glow circle.
const amulet: readonly ShapeSpec[] = [
  { kind: 'ring', r: 9, stroke: 0xffc9a24a, strokeWidth: 2, glow: 'friendly' },
  { kind: 'circle', r: 3, fill: 0xffff2b2b },
];

Bounds

import { shapeBoundsRadius, singleShapeRadius, type ShapeSpec } from '@flare-engine/shapes';

declare const playerShip: ShapeSpec;

// For atlas tile sizing — max half-extent across a composition. Includes each
// shape's ox/oy offset, a stroke-width margin where a stroke is actually
// painted, and the full extent of a glow ring (+5.5 friendly, +2.25 hazard),
// so a tile sized from this fully contains what `drawShape` paints.
const tileRadius = shapeBoundsRadius([playerShip]);

// For glow halo sizing — radius of one primitive.
const glowR = singleShapeRadius({ kind: 'circle', r: 14 });

Adding a new primitive

Three ways, in order of friction:

  1. Use the path escape hatch. SVG d="..." strings parse natively via Skia.Path.MakeFromSVGString. Same syntax as Figma / Inkscape / <path>. Zero engine change.

  2. Author a composition. Wrap multiple primitives in a ShapeSpec[] array. No new code at all.

  3. Add a named primitive. When (1)/(2) aren't ergonomic enough or you need a perf-optimized branch:

    1. Extend the ShapeSpec union in src/types.ts
    2. Add a case 'kind': branch in drawShape (src/draw.ts)
    3. Add a bounds branch in singleShapeRadius (src/bounds.ts)
    4. Add tests in tests/draw.test.ts and tests/bounds.test.ts

    Tsc catches a missing step 3: singleShapeRadius returns number, so an unhandled kind fails to compile. It does not catch a missing step 2 — drawShape returns void and its switch has no default, so a new kind without a draw branch compiles and silently draws nothing.

Glow tags

Two visual halos for free, set glow: 'friendly' | 'hazard' on any primitive:

| Tag | Color | Use | |---|---|---| | friendly | safe-green #6FA66B at α=0.25, single 3 px ring at r+4 | Player ships, pickups, safe markers | | hazard | hazard-orange #E84B2A at α=0.6, double 0.5 px ring at r+1 and r+2 | Enemy bullets, danger cues |

License

Apache-2.0