Image editor SDK · Rust core, one binary everywhere
Edit small. Render big. Prove it.
The edit log becomes the C2PA manifest. Your key signs it. Your users edit a working copy in the browser; your backend renders the same log on the 80-megapixel original, with the same core the browser runs. Same original, same log, same bytes, verified by hash.
Everything below is CSS. Copy it, paste it into your app, done.
<sealpixel-editor theme="dark" locale="en">
Loading the editor…
The editor could not load here. Try the demo instead.
Every edit on record. Signed by you.
A photo you publish carries its own history: what was blurred, what was cropped, and who signed it. Anyone can check it, in the browser, in seconds. The key stays with you. Sealpixel never sees a key.
Nothing extra for your editors
Pixelate the plate, crop, publish. The record writes itself from the edits. Nobody fills in a form, nothing is forgotten.
Your name on it
The credential carries your signature, from the key you already hold. Nobody can sign in your name, and a change made afterwards shows.
Checked anywhere
Content Credentials are an open standard. A reader opens the image in any viewer that supports them, the public Verify page included, and sees the history and the signer.
What a reader sees
Signed by your organisation
Edited: a region obscured, pixelated
Cropped
Original: dsc-4821
Edited with Sealpixel
The entries above are what the manifest below says, in the words a viewer uses.
@sealpixel/c2pa
The manifest definition. Free, under the same licence as the rest.
@sealpixel/c2pa-sign
The signing module. Provenance plan, per organisation.
For your developers: the edit log and the manifest definition
The edit log becomes the C2PA manifest definition, one action per edit with its region in percent of the output. @sealpixel/c2pa writes it, free. @sealpixel/c2pa-sign signs it where your key is.
Two actions. c2pa.edited carries the region, moved through the crop into percent of the output; c2pa.cropped names the new frame. A viewer shows them as Content Credentials.
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
Prove what changed
Every blur and crop goes on record as the photo is edited, and your own key signs the record. A reader checks it in the browser. Sealpixel never sees a key.
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 in the order of the toolbar, each its own package. Drop, replace or extend any of them.
Crop
Cut to a ratio, turn by any angle, flip.
@sealpixel/plugin-crop
Finetune
Adjust brightness, contrast, saturation, exposure, gamma, clarity and vignette.
@sealpixel/plugin-finetune
Filter
Apply grayscale, sepia, vintage, cool, warm or any 4×5 colour matrix, at any strength.
@sealpixel/plugin-filter
Assist
Opt-in
Let your own detector propose regions in the browser; a person confirms, and plain redactions reach the log.
@sealpixel/plugin-assist
Redact
Pixelate or blur rectangles and freeform regions, with cells on the original’s grid.
@sealpixel/plugin-redact
Annotate
Draw rectangles, ellipses, lines, arrows, freehand paths and text, all movable after the fact.
@sealpixel/plugin-annotate
Sticker
Place logos and image assets at any angle and opacity.
@sealpixel/plugin-sticker
Frame
Add a solid, inset or polaroid frame.
@sealpixel/plugin-frame
Watermark
Stamp an image or text in a corner or tiled, identical in the backend render.
@sealpixel/plugin-watermark
Resize
Fit the output to a target size: contain, cover or fill, Lanczos3 down and Catmull-Rom up.
@sealpixel/plugin-resize
Fill
No toolbar button
Colour the areas a rotation or crop leaves empty.
@sealpixel/plugin-fill
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
Buying for a company? Enter your company name and VAT number in the checkout under “Add VAT number”, so the invoice carries them.
Provenance
For pipelines that sign
€7,490
per organisation and year, net
Signing in production with @sealpixel/c2pa-sign, seats included. Always paid, whatever your revenue; the manifest definition in @sealpixel/c2pa stays free.
@sealpixel/c2pa-sign in production
Four developer seats, the entry tier; more at the tier price of your total seat count
Assurance that the action mapping matches the C2PA specification version the docs name
Priority on changes the specification forces
Help with c2patool or c2pa-rs, and email support
Business Source License 1.1 without the free tier. The Sealpixel Provenance License is its commercial licence. Read the license
Buying for a company? Enter your company name and VAT number in the checkout under “Add VAT number”, so the invoice carries them.
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 renewal date; if they reach a lower tier, it applies to every seat from that day, prorated for the rest of the term and 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.
Do you hold my signing key?
No. Your own code signs. Sealpixel writes the manifest definition from the edit log, and the signing module @sealpixel/c2pa-sign runs where your key is: it hands your function the bytes to sign and takes the signature back. The key never enters it. Sealpixel is no certificate authority and keeps no key.
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.