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

@nomideusz/svelte-geometrize

v0.6.0

Published

Geometric image placeholders for Svelte 5 — triangles resolve into the real photo as it loads. Build-time shape fitting, tiny runtime.

Downloads

467

Readme

@nomideusz/svelte-geometrize

npm license

Geometric image placeholders for Svelte 5 — instead of a blur, triangles resolve into the photo while it loads, geometrize.co.uk-style.

Live demo → svelte-geometrize.vercel.app · In production on szkolyjogi.pl, where 700+ listing heroes paint an instant geometric preview of the photo while it loads (open any school page, e.g. this one — hard-refresh to replay), and on kurcz.pl.

The expensive shape fitting (hill-climbing, via geometrizejs) runs at build time and emits a small ordered shape list (~1–10 KB raw, far less gzipped). Because geometrize is iterative — shape 1 is the dominant region, shape 100 is fine detail — replaying the shapes in fit order makes the placeholder visibly sharpen until the real image crossfades in. The runtime component is tiny and dependency-free (sharp is an optional peer for the Node / Vite / CLI half only).

Usage

pnpm add @nomideusz/svelte-geometrize
# only needed for the Vite plugin, Node API, or CLI:
pnpm add -D sharp

Register the Vite plugin (build-time half):

// vite.config.ts
import { geometrize } from '@nomideusz/svelte-geometrize/vite';

export default defineConfig({
	plugins: [geometrize(), sveltekit()]
});

Then in a component:

<script lang="ts">
	import { GeometrizedImage } from '@nomideusz/svelte-geometrize';
	import placeholder from './photo.jpg?geometrize';
	import src from './photo.jpg';
</script>

<GeometrizedImage {placeholder} {src} alt="Sunset over mountains" revealMs={850} loading="lazy" />

For typed ?geometrize imports, either copy the declarations into app.d.ts or reference the shipped ambient types:

/// <reference types="@nomideusz/svelte-geometrize/client" />

Options

Plugin-wide defaults via geometrize({ ... }), per-image overrides via query params:

./photo.jpg?shapes=150&alpha=160&maxSize=160&shapeTypes=triangle,ellipse&seed=1&geometrize

Param order doesn't matter to the plugin, but keeping geometrize last lets the *&geometrize module declaration type these imports.

| Option | Default | Meaning | | --- | --- | --- | | shapes | 100 | Max shapes to fit — more detail, bigger payload | | shapeTypes | ['triangle'] | Any of rectangle, rotated-rectangle, triangle, ellipse, rotated-ellipse, circle, line, quadratic-bezier | | alpha | 128 | Shape opacity, 0–255 | | maxSize | 128 | Longest edge the image is downscaled to before fitting (the SVG scales back up losslessly) | | candidateShapesPerStep | 50 | Fit quality vs. build speed | | shapeMutationsPerStep | 100 | Fit quality vs. build speed | | seed | 1 | PRNG seed for reproducible fits (false = non-deterministic) | | targetScore | — | Stop early once approximation score ≤ this (lower = closer) | | cacheDir | node_modules/.cache/svelte-geometrize | Persistent disk cache (plugin only); false disables |

Fits are cached on disk by file content hash + resolved options, so clean rebuilds skip already-fitted images. Concurrent loads of the same key coalesce.

Component props beyond placeholder / src / alt: reveal ('fade' | 'pop' | 'scatter', default 'fade'), revealMs (total ms until the last shape starts — preferred over raw stagger), stagger (ms between shapes, default 15), shapeDuration (per-shape fade, default 400), fadeDuration (crossfade to the real image, default 600), objectFit (cover | contain | fill | …, default cover), objectPosition (default center). onload / onerror are forwarded (chained after the internal reveal). All other props go to the <img>. Width/height default from the placeholder. The reveal is pure CSS, plays with SSR before hydration, and respects prefers-reduced-motion.

Node API

import {
	generatePlaceholder,
	generatePlaceholderFromUrl,
	placeholderToSvg,
	placeholderToDataUri
} from '@nomideusz/svelte-geometrize/node';

const placeholder = await generatePlaceholder('photo.jpg', { shapes: 80, seed: 1 });
const fromCms = await generatePlaceholderFromUrl('https://cdn.example/hero.jpg');
const svg = placeholderToSvg(placeholder);

Requires the optional peer sharp.

Browser / worker fitter

import { fitShapes } from '@nomideusz/svelte-geometrize/fit';

// rgba from canvas getImageData / a worker — no sharp
const placeholder = fitShapes(rgba, width, height, sourceW, sourceH, { shapes: 60 });

placeholderToSvg / placeholderToDataUri are also exported from the root entry (no geometrize/sharp) for CSS backgrounds or og-images.

CLI

npx svelte-geometrize photo.jpg -o photo.json
npx svelte-geometrize ./photos --out ./placeholders --shapes 80 --target-score 0.12

Dynamic images (runtime srcs, DB-stored placeholders)

The Vite plugin covers images known at build time. For photos that live behind an API — CMS content, user uploads, scraped listings — generate the placeholder once at ingest (or in a backfill script) with the Node API / CLI, store the JSON next to the record, and pass it straight to the component. This is how szkolyjogi.pl does its listing heroes:

import { generatePlaceholder } from '@nomideusz/svelte-geometrize/node';

const bytes = Buffer.from(await (await fetch(photoUrl)).arrayBuffer());
const placeholder = await generatePlaceholder(bytes);
await db.update(listings)
	.set({ photoPlaceholder: JSON.stringify(placeholder) })
	.where(eq(listings.id, id));
<GeometrizedImage
	placeholder={listing.photoPlaceholder}
	src="/api/photo/{listing.id}"
	alt={listing.name}
	revealMs={900}
/>

A placeholder is ~2–10 KB raw (a few KB gzipped), so inlining it in server-rendered HTML is cheap — the geometric preview paints before the photo's first byte arrives, no layout shift, and sharp stays a server-side ingest dependency, never shipped to the client.

Demo

pnpm install
pnpm dev