Image editor SDK · Rust core, one binary everywhere
Edit small. Render big. Identical.
Your users edit a working copy in the browser. Your backend renders the same edit log on the 80-megapixel original, with the same core the browser runs. Same original, same log, same bytes. Verified by hash.
The browser edits a working copy. Your backend renders the original.
Above 16 megapixels, by default, the editor keeps only a working copy of at most 2048 px and previews every edit on it. Your backend takes the edit log and renders the full resolution with @sealpixel/render, the same core the browser runs.
In the browser
2048 × 1537
3.15 MP working copy. The browser decodes the original once and keeps only this.
Over the wire
561 bytes
Not an image. Five operations, in coordinates that don’t care about resolution.
On your backend
10328 × 7752
80.1 MP from the original, in about 2,178 MB: 24 bytes per pixel plus 256 MB. No browser runtime.
A 30 MB original against the handful of bytes that say what to do with it. That ratio is the architecture.
Your backend renders what the browser showed
@sealpixel/render is a building block, not a server. Your route, function or job passes the original’s bytes and the edit log. The renderer checks both, renders with the same WebAssembly binary as the browser and returns the bytes, their hash, the size and the timings. Routing, storage and retries stay in your code.
Limits before the decode: file size, pixel count from the header, log size
The log belongs to its original: another sha256 or another size is refused
Same plugins, same options: a log that breaks the editor’s rules is refused
No routing, no CORS, no storage: you pass every byte, fonts included
import { createRenderer, estimateMemoryMB } from '@sealpixel/render';
import { defaultPlugins } from '@sealpixel/plugins';
// Once per process, in your own route, function or job.
const renderer = createRenderer({
plugins: defaultPlugins, // the list the editor registers
limits: { maxMegapixels: 100 }, // default 80; this photo has 80.1
});
const { log, index } = saved; // from the editor's getEditLog()
const result = await renderer.render({
original, // Uint8Array, byte for byte
editLog: log,
upTo: index,
output: { format: 'image/jpeg', quality: 0.92 },
});
// { bytes, stream(), hash, width, height, mimeType, timings }
// result.hash equals a browser export of the same original and log
estimateMemoryMB(10328, 7752); // 2178
One component. Three products.
The same element with different plugins, themes and languages. No fork, no build step: attributes and CSS.
Loading the editor…
The editor could not load here. Try the demo instead.
Profile photo
A signup flow: round crop, brighten, done. Two tools, light theme, no export button; the export keeps only the circle.
The editor could not load here. Try the demo instead.
Marketing studio
A content team building campaign images: every tool, dark graphite, pill buttons.
<sealpixel-editor theme="dark" locale="en" active-tool="filter" show-download="never"
plugin-options='{"filter":{"presets":["grayscale","sepia","vintage","warm",{"name":"brand","label":"Brand","matrix":[[0.8,0.1,0.1,0,0.02],[0.05,0.85,0.1,0,0],[0.1,0.1,0.9,0,0.05],[0,0,0,1,0]]}],"thumbnails":true}}'></sealpixel-editor>
<style>
sealpixel-editor::part(tool) { border-radius: 999px; }
</style>
<script type="module">
import '@sealpixel/element'; // registers the element and the bundled worker
import { allPlugins } from '@sealpixel/plugins';
const editor = document.querySelector('sealpixel-editor');
editor.plugins = allPlugins; // sticker and watermark ship, but are not in the default set
editor.addEventListener('sealpixel:process', (e) => {
upload(e.detail.bytes, e.detail.hash);
});
</script>
Loading the editor…
The editor could not load here. Try the demo instead.
Compliance desk
A compliance team pixelating personal data in scanned forms. Redact, annotate and watermark, high-contrast theme, German UI.
The same component lays itself out for a phone on its own. No second UI, no mobile flag. Try it in the frame.
One component, two layouts
Below 768 px of container width the toolbar drops to the bottom and the tool panel sits right above it. It follows its container, not the viewport, so a narrow sidebar gets the phone layout too.
Touch first
Pinch to zoom, drag to pan, one finger for crop handles, redaction boxes and annotations. Wheel and trackpad do the same on a desktop.
Decode once, then nothing
The phone decodes the image once and edits from memory. No re-upload between tools, no round trip for a preview. Your battery will notice.
Notches and home indicators
Safe-area insets are respected. show-history="buttons" drops the timeline and show-export="never" hides the export button, so the essentials fit a small screen.
9:41
Loading the editor…
The editor could not load here. Try the demo instead.
Live editor in a phone frame. Drag, pinch, tap.
The edit log is the image
One log, one core
Every crop, filter and annotation is an operation in a JSON log. A Rust core compiled to WebAssembly runs it in a Web Worker, on Node, Deno, Bun or AWS Lambda. Same bytes in, same bytes out.
Byte-identical, hash-verified
Same source, same log, rendered anywhere: the SHA-256 matches. In the browser, on Node, Deno, Bun or in a Lambda.
Pick up from JSON
Store the original once and the log next to it. Reopen any state on any device, with no generation loss. The log can travel inside the exported PNG, JPEG or WebP.
Every tool your users expect
Eleven plugins ship in the box, ten of them in the default set. Each one is its own package you can drop, replace or extend.
Crop
Rotate by any angle, flip, aspect presets. Handles stay 7 px on every screen.
@sealpixel/plugin-crop
Finetune
Brightness, contrast, saturation, exposure, gamma, clarity and vignette.
@sealpixel/plugin-finetune
Filter
Grayscale, sepia, vintage, cool, warm and any 4×5 colour matrix, with strength.
@sealpixel/plugin-filter
Redact
Pixelate or blur rectangles and freeform regions, then resize or rotate them by handles. Cells snap to the original’s grid, so working copy and original match.
@sealpixel/plugin-redact
Annotate
Rectangles, ellipses, lines, arrows, freehand paths and text, all resizable and rotatable after the fact. Fonts are hashed assets, so glyphs are identical everywhere.
@sealpixel/plugin-annotate
Sticker
Place logos and image assets, rotate, flip, set opacity.
@sealpixel/plugin-sticker
Frame
Solid, inset and polaroid frames.
@sealpixel/plugin-frame
Watermark
Image or text, anchored to a corner or tiled. Identical in your backend render.
@sealpixel/plugin-watermark
Resize
Contain, cover and fill to a target size. Lanczos3 down, Catmull-Rom up.
@sealpixel/plugin-resize
Fill
Background colour for the areas a rotation or crop leaves empty.
@sealpixel/plugin-fill
Assist
A detector proposes regions on the working copy, a person confirms, and ordinary redactions reach the log. Your own model, loaded on demand. Nothing leaves the browser. Opt-in.
@sealpixel/plugin-assist
A component for every stack
A Web Component underneath, thin typed wrappers on top. Controlled and uncontrolled edit logs everywhere.
import { readFile } from 'node:fs/promises';import { DEFAULT_FONT, defaultPlugins } from '@sealpixel/plugins';import { createRenderer } from '@sealpixel/render';// The renderer reads no file. You pass every byte, the font too.const font = new Uint8Array(await readFile(new URL(DEFAULT_FONT.url)));// Once per process. Your route, function or job calls render() per request.const renderer = createRenderer({ plugins: defaultPlugins, assets: [{ id: DEFAULT_FONT.id, bytes: font }],});const { bytes, hash } = await renderer.render({ original, editLog: log, upTo: index });
Built for developers, documented like it
Headless core, typed edit log, a plugin SDK with a testing kit, and docs with live editors on every page.
A plugin is an npm package: operations with Zod schemas, a reducer, a kernel in JavaScript or in Rust compiled to its own WebAssembly module, and an optional tool with panel and gizmo. It runs on the client and on the server without rebuilding the core.
Scaffold one
npm create @sealpixel/plugin acme-spotlight -- --kernel wasm-rustcd acme-spotlight && npm test # parity: Node vs Chromium, WebKit, Firefox
The tier follows your total number of seats, and its price applies to every one of them. Seats you add later are prorated to your existing renewal date. If the total reaches a lower tier, that tier applies to every seat from the day you add them: prorated for the rest of the term, in full at the next renewal. The part of the term already gone is not recalculated.
Business Source License 1.1, Apache 2.0 after four years. No runtime licence check: the software never phones home. Read the license
Our order process is conducted by our online reseller Paddle.com. Paddle is the Merchant of Record for all our orders and handles VAT, GST and sales tax. We stay the licensor of the software. Paddle Buyer Terms
Questions
Who gets Sealpixel for free?
Anyone under EUR 1,000,000 of gross annual revenue: individuals, and organizations below that threshold. Production and commercial products are included. Revenue counts across the whole group, so a subsidiary of a large company is not free. For client work the client’s revenue decides, so a freelancer building for a small startup pays nothing and the same freelancer building for a large customer is covered by that customer’s seats.
I am an agency. What do I pay?
One seat per developer who works on codebases that include Sealpixel, regardless of how many clients or products those codebases serve. A seat held by your developer covers all their client work, so your clients need no seats of their own, and none to run the finished application.
How do volume tiers work?
Tell us how many seats you need. 1 to 4 seats cost 999 EUR each per year, 5 to 9 cost 899, 10 to 24 cost 799, and the tier applies to every seat you hold. Seats added later are prorated to the renewal date you already have, and if the total reaches a lower tier, that tier applies to every seat from the day you add them — prorated for the rest of the term and in full at the next renewal. The part of the term already gone is not recalculated. From 25 seats we quote individually.
Can an open-source project use Sealpixel?
Yes. Sealpixel itself is source available under the Business Source License 1.1, not open source, so a project that depends on it inherits those terms: contributors below the revenue threshold need no seats, a company that develops the project needs seats for its developers, and copies and forks stay under the BUSL. Every release becomes Apache 2.0 four years after it was published.
Is Sealpixel open source?
No. Sealpixel is source available under the Business Source License 1.1. The source is public and you may read, modify, fork and redistribute it, and production use is free below the revenue threshold — but the BUSL is not an OSI-approved license, so calling it open source would be wrong. Four years after a version is published it converts to the Apache License 2.0, and that version is then open source in the OSI sense.
What exactly is a developer seat?
A seat belongs to a named person who writes, reviews, tests or integrates code in a project that includes Sealpixel. It can be reassigned when that person leaves. Designers in the codebase and QA engineers count. End users of your application never need a seat, whatever their number, and neither do people who only operate or administer it.
Can I build a SaaS on Sealpixel?
Yes. The only limit is a service whose primary value is the image editor itself, such as a hosted "edit your image" product or an image editing API sold to third parties. A photo-book shop, a CMS, a real-estate portal or a support tool with editing built in are all fine.
Who am I actually buying from, and who sends the invoice?
From Paddle. Our order process is conducted by our online reseller Paddle.com, which is the Merchant of Record for all our orders: you buy the seats from Paddle, Paddle invoices you and calculates, collects and remits VAT, GST or sales tax for your country. Enter your VAT or tax ID at the checkout so the right treatment applies. We stay the producer and licensor of Sealpixel — the software license is granted by us under the Developer Seat License, never by Paddle.
How does the refund work?
Cancel within 30 days of your first purchase and you get the full amount back, by the same route the payment came in. Paddle handles the refund as our Merchant of Record; write to us or to Paddle, we approve it either way. No questions asked.
What exactly is byte-identical?
The same source bytes plus the same edit log rendered through the same core version produce the same output bytes and SHA-256, in every browser, on Node, Deno, Bun and Lambda. So a browser export and a render with @sealpixel/render of the same original match byte for byte. The preview on a working copy is a different input: it has the same composition (same normalized geometry), but not the same bytes as the render of the original. The docs spell this out.
How big is the download?
The WebAssembly core is about 1.3 MB raw and 397 KB with Brotli, loaded lazily when the editor mounts. The Lit UI and the render worker add a few tens of kilobytes. There is a budget check in CI.
Which browsers are supported?
Current Chrome, Edge, Safari and Firefox on desktop and mobile. The core uses WebAssembly SIMD, so the floor is Chrome 91, Firefox 89 and Safari 16.4. Every commit runs the parity suite in Chromium, WebKit and Firefox, and the UI suite in Chromium and WebKit. On iOS Safari the download falls back to the share sheet.
Can I write my own tool?
Yes. npm create @sealpixel/plugin scaffolds a package with operations, a reducer, a JavaScript or Rust kernel and a tool panel. The testing kit renders your plugin in Node and three browsers and asserts equal hashes. Two example plugins ship in the repository.