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

pictel

v0.3.0

Published

Pictel is a framework for Photoshop-like image editing via React code. It provides primitives for comprehensive pixel processing, live preview, and headless rendering.

Readme

pictel

Pictel is a framework for Photoshop-like image editing via React code. It provides primitives for comprehensive pixel processing, live preview, and headless rendering.

Install

npm install pictel @pictel/effects

Packages

  • @pictel/effects — the effect library: colour grading, blurs, blend modes, halftone, displacement, line-integral convolution, generative sources, and more.
  • @pictel/ml — ML-powered effects via Transformers.js + WebGPU: background removal, segmentation, depth maps, and upscaling.
  • @pictel/cli — headless renderer (Puppeteer + Sharp) that exports compositions to PNG/JPEG/WebP/AVIF.

A corpus of worked examples — each a single composition with its intent, before/after images, and full source — lives in apps/demos/out/.

Quick start

import { Canvas, Clip, Image } from "pictel";
import { Blur } from "@pictel/effects";

export default () => (
	<Canvas dimensions={{ width: 800, height: 800 }}>
		<Clip>
			<Blur radius={4}>
				<Image src="/photo.jpg" />
			</Blur>
		</Clip>
	</Canvas>
);

Examples

Oil Painting

| Before | After | | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | | |

import { Direction, Duotone, Hatch } from "@pictel/effects";
import { Canvas, Image } from "pictel";
import headshot from "../../assets/headshot.jpg";

const INK: [number, number, number] = [38, 30, 54];
const PAPER: [number, number, number] = [240, 234, 220];

export default function OilPainting() {
	return (
		<Canvas
			mode="display"
			dimensions={{ width: 640, height: 640 }}
		>
			<Duotone
				dark={INK}
				light={PAPER}
			>
				<Hatch
					bands={4}
					spacing={[5, 8, 12, 16]}
					length={24}
					uniformStep
					map={
						<Direction mode="structure">
							<Image
								src={headshot}
								width={640}
								height={640}
								fit="cover"							/>
						</Direction>
					}
				>
					<Image
						src={headshot}
						width={640}
						height={640}
						fit="cover"
					/>
				</Hatch>
			</Duotone>
		</Canvas>
	);
}

Pop Art

| Before | After | | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | | |

import { Contrast, Halftone, Multiply, Outline, Saturate, Threshold } from "@pictel/effects";
import { Canvas, Image } from "pictel";
import photo from "../../assets/Golden Hour Portrait.jpg";

export default function PopArt() {
	return (
		<Canvas
			mode="display"
			dimensions={{ width: 640, height: 960 }}
		>
			<Multiply
				apply={
					<Threshold threshold={140}>
						<Outline
							sigma={2.4}
							k={1.6}
							epsilon={0.005}
							phi={200}
						>
							<Image
								src={photo}
								width={640}
								height={960}
								fit="cover"							/>
						</Outline>
					</Threshold>
				}
			>
				<Halftone
					colorMode="color"
					dotSize={10}
				>
					<Contrast amount={1.35}>
						<Saturate amount={2.4}>
							<Image
								src={photo}
								width={640}
								height={960}
								fit="cover"							/>
						</Saturate>
					</Contrast>
				</Halftone>
			</Multiply>
		</Canvas>
	);
}

Tilt-Shift

| Before | After | | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | | |

import { Blur, Brightness, Contrast, Invert, Saturate } from "@pictel/effects";
import { DepthMap } from "@pictel/ml";
import { Canvas, Clip, Image } from "pictel";
import cityPhoto from "../../assets/city overview.jpg";

export default function TiltShift() {
	return (
		<Canvas
			mode="display"
			dimensions={{ width: 1024, height: 683 }}
		>
			<Clip>
				<Blur
					radius={7}
					mode="parameter"
					map={
						<Invert>
							<Brightness amount={2}>
								<Contrast amount={0.35}>
									<DepthMap>
										<Image
											src={cityPhoto}
											width={1024}
											height={683}
											fit="cover"
										/>
									</DepthMap>
								</Contrast>
							</Brightness>
						</Invert>
					}
				>
					<Saturate amount={1.1}>
						<Contrast amount={1.1}>
							<Image
								src={cityPhoto}
								width={1024}
								height={683}
								fit="cover"							/>
						</Contrast>
					</Saturate>
				</Blur>
			</Clip>
			<div
				style={{
					position: "absolute",
					inset: 0,
					boxShadow: "inset 0 0 100px 30px rgba(0,0,0,0.5)",
					pointerEvents: "none",
				}}
			/>
		</Canvas>
	);
}

Banknote Print

| Before | After | | --- | --- | | | |

import { Bilateral, Brightness, DisplacementMap, Duotone, Engrave } from "@pictel/effects";
import { DepthMap, RemoveBackground } from "@pictel/ml";
import { Canvas, Image } from "pictel";

const HEADSHOT_URL = "https://pictel-demos.s3.us-east-1.amazonaws.com/sources/headshot.jpg";

const INK: [number, number, number] = [24, 56, 38];
const CREAM: [number, number, number] = [234, 230, 213];
const canvasW = 512;
const canvasH = 512;

export default function Banknote() {
	const subject = (
		<RemoveBackground>
			<Image src={HEADSHOT_URL} width={canvasW} height={canvasH} fit="cover" />
		</RemoveBackground>
	);

	return (
		<Canvas
			mode="display"
			dimensions={{ width: canvasW, height: canvasH }}
			style={{ backgroundColor: `rgb(${String(CREAM[0])}, ${String(CREAM[1])}, ${String(CREAM[2])})` }}
		>
			<Duotone dark={INK} light={CREAM}>
				<DisplacementMap scaleX={10} scaleY={14} map={<DepthMap>{subject}</DepthMap>}>
					<Engrave spacing={5} relief={0}>
						<Brightness amount={1.35}>
							<Bilateral spatialSigma={4} colorSigma={60}>
								{subject}
							</Bilateral>
						</Brightness>
					</Engrave>
				</DisplacementMap>
			</Duotone>
		</Canvas>
	);
}

Night Vision

| Before | After | | --- | --- | | | |

import { Contrast, Duotone, Grain, LinePattern, Multiply } from "@pictel/effects";
import { Canvas, Image } from "pictel";

const LANDSCAPE_URL = "https://pictel-demos.s3.us-east-1.amazonaws.com/sources/evening-landscape.jpg";

const W = 1024;
const H = 1536;

const DARK_GREEN: [number, number, number] = [4, 18, 8];
const PHOSPHOR_GREEN: [number, number, number] = [120, 240, 110];

export default function NightVision() {
	return (
		<Canvas mode="display" dimensions={{ width: W, height: H }}>
			<Grain intensity={24} seed={6173}>
				<Multiply
					apply={
						<LinePattern
							width={W}
							height={H}
							seed={0}
							spacing={3}
							thickness={1}
							angle={0}
							color="rgb(40, 80, 30)"
							background="rgb(230, 240, 220)"
						/>
					}
				>
					<Duotone dark={DARK_GREEN} light={PHOSPHOR_GREEN}>
						<Contrast amount={1.35} mode="parameter">
							<Image src={LANDSCAPE_URL} width={W} height={H} fit="cover" />
						</Contrast>
					</Duotone>
				</Multiply>
			</Grain>
		</Canvas>
	);
}

Risograph Print

| Before | After | | --- | --- | | | |

import { Quantize } from "@pictel/effects";
import { Canvas, Image } from "pictel";

const PORTRAIT_URL = "https://pictel-demos.s3.us-east-1.amazonaws.com/sources/portrait-with-background-1.jpg";

const W = 1024;
const H = 1536;

const RISO_PALETTE = [
	[245, 240, 230],
	[235, 60, 130],
	[40, 80, 180],
	[35, 35, 40],
] as const;

export default function RisographPrint() {
	return (
		<Canvas mode="display" dimensions={{ width: W, height: H }}>
			<Quantize palette={RISO_PALETTE} dither="bayer-8">
				<Image src={PORTRAIT_URL} width={W} height={H} fit="cover" />
			</Quantize>
		</Canvas>
	);
}

Effect output cache

Pictel keeps an IndexedDB-backed cache of effect outputs, keyed by the input pixels plus a version string. When inputs are byte-identical to a prior run and the version is unchanged, the cached pixels are rehydrated and the effect's apply callback is not called. The cache persists across HMR cycles and full page refreshes — ML effects (RemoveBackground, DepthMap, ...) benefit most, but every effect that publishes a version participates.

Every standard effect in @pictel/effects and @pictel/ml accepts an optional version?: string prop and composes it with an internal version that captures its own props. Caching is on by default; bumping version at any layer invalidates that node and everything its output feeds into.

import { Canvas, Image } from "pictel";
import { RemoveBackground } from "@pictel/ml";

<RemoveBackground version="v2">
	<Image src="/portrait.jpg" width={1024} height={1024} fit="cover" />
</RemoveBackground>;

Entries are bounded by a per-entry size cap (50 MB default — rejects pathologically large outputs at write) and a TTL (24 h default — expired entries are treated as misses on read and deleted inline). Both are configurable on createEffectCache; pass ttlMs: Infinity to disable expiry.

Effects that produce bleed (Blur, DropShadow, Bloom, Outline, certain Hatch / LIC configurations) skip the cache write in v1 — the entry shape stores output pixels only. See design-effect-output-cache for the full contract and rejected alternatives.

API reference below — generated from JSDoc on the source.