morphicons

Showcase

canvasTarget draws the morph into any canvas or 2D context: a texture inside a map, a sprite in a chart, a favicon, an OffscreenCanvas in a worker.

Why this path
Icons where the DOM can't reach: WebGL scenes, canvas charts, workers. Every icon shares the driver's single rAF.
How it works
Each frame is one stroke() of a Path2D. Hosts with their own render loop give the icon a small canvas of its own and composite it; onWrite says when to repaint.
The tradeoff
You own resolution and devicePixelRatio, and it draws strokes only. Color is fixed at target creation, so a live tint happens at composite time (both demos below do that). canvasTarget itself is 0.5 KB gzip.

A streaming chart · TanStack Charts

The trend icon follows the series: when the trend turns, the icon and its color turn with it. Nudge the drift to flip it.

Map pins with states · Mapbox GL

Each pin is a Mapbox image drawn by a morphing canvas. Drop one, hover it, save it.

The whole integration

One adapter call around the DOM driver. The rest of the demos above is the host compositing these pixels.

morphicons/adapters
import { createMorph } from "morphicons/dom";
import { canvasTarget } from "morphicons/adapters";
import { Flag, MapPin } from "lucide"; // data, not components

const sprite = document.createElement("canvas");
sprite.width = sprite.height = 64;

const morph = createMorph(
  canvasTarget(sprite, { color: "#fff", onWrite: () => map.triggerRepaint() }),
  MapPin,
);
morph.morphTo(Flag, "smooth");

// the canvas is pixels you own: a Mapbox StyleImage, a chart badge,
// a WebGL texture, a favicon, an OffscreenCanvas in a worker

Mapbox GL and the chart runtime load only when their card scrolls into view. The save wave is Canvas UI's Ripple shader (MIT + Commons Clause).