Skip to content
Sealpixel
de

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.

Preset
<sealpixel-editor theme="dark" locale="en">

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.

See the Provenance plan →How it works, for developers →

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.

The edit log

{
  "version": 1,
  "source": { "id": "dsc-4821", "width": 6000, "height": 4000 },
  "ops": [
    { "type": "redact.add", "id": "r1",
      "region": { "kind": "rect",
        "rect": { "x": 0.42, "y": 0.31, "width": 0.07, "height": 0.09 } },
      "mode": "pixelate", "amount": 64 },
    { "type": "crop.set", "id": "c1",
      "rect": { "x": 0.1, "y": 0.1, "width": 0.8, "height": 0.8 } }
  ]
}

Two operations: a plate pixelated, then a crop.

The manifest definition

{
  "title": "result.jpg",
  "assertions": [{
    "label": "c2pa.actions",
    "data": {
      "allActionsIncluded": true,
      "softwareAgents": [{ "name": "sealpixel", "version": "1.0.0-rc.20" }],
      "actions": [
        {
          "action": "c2pa.edited",
          "description": "Region obscured: pixelate, 64 px",
          "digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/humanEdits",
          "softwareAgentIndex": 0,
          "parameters": { "dev.sealpixel.redact": { "mode": "pixelate", "amount": 64 } },
          "changes": [{
            "description": "obscured (pixelate)",
            "region": [{
              "type": "spatial",
              "shape": { "type": "polygon", "unit": "percent",
                "origin": { "x": 40, "y": 26.25 },
                "vertices": [{ "x": 40, "y": 26.25 }, { "x": 48.75, "y": 26.25 },
                             { "x": 48.75, "y": 37.5 }, { "x": 40, "y": 37.5 }] }
            }]
          }]
        },
        {
          "action": "c2pa.cropped",
          "description": "Cropped to 4800 x 3200 of 6000 x 4000",
          "digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/humanEdits",
          "softwareAgentIndex": 0
        }
      ]
    }
  }]
}

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.

  • 139

    golden renders

    edit logs and plans with pinned hashes

  • 136

    CLI parity cases

    native Rust and Node produce the same bytes

  • 130

    browser test runs

    across 3 engines, on every commit

  • 397 KB

    compressed core

    1.3 MB raw, one binary for every runtime

Runs identically on Chrome, Safari, Firefox, Node.js, Deno, Bun, AWS Lambda

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.

The full edit log

561 bytes serialised

{
  "version": 1,
  "source": {
    "id": "dsc-4821", "width": 10328, "height": 7752,
    "hash": "9f2a7c4e18b3d05a6e1f8c2b47d930ae5c86f1b2093d4e7a8c5b1f6d2e0a3947"
  },
  "ops": [
    { "type": "crop.set",     "id": "c1", "rect": { "x": 0.08, "y": 0.12, "width": 0.8, "height": 0.72 } },
    { "type": "finetune.set", "id": "f1", "params": { "contrast": 0.18, "saturation": 0.22 } },
    { "type": "redact.add",   "id": "r1", "region": { "kind": "rect", "rect": { "x": 0.42, "y": 0.31, "width": 0.07, "height": 0.09 } }, "mode": "pixelate", "amount": 64 },
    { "type": "filter.set",   "id": "p1", "preset": "warm", "strength": 0.4 },
    { "type": "resize.set",   "id": "s1", "width": 3000 }
  ]
}

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

Try it in the demo →Read the render guide →

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.

Profile photo

A signup flow: round crop, brighten, done. Two tools, light theme, no export button; the export keeps only the circle.

Marketing studio

A content team building campaign images: every tool, dark graphite, pill buttons.

Compliance desk

A compliance team pixelating personal data in scanned forms. Redact, annotate and watermark, high-contrast theme, German UI.

Works on a phone like it works on a desktop.

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.

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.

Render on your server

A building block, not a server: call it from your own route, function or job. Node 24+, Deno, Bun and AWS Lambda.

Open in StackBlitz
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.

The same edit log, in your framework

<script type="module">
  import '@sealpixel/element';
  const editor = document.querySelector('sealpixel-editor');
  editor.src = '/photo.jpg';
  editor.addEventListener('sealpixel:process', (e) => {
    const { bytes, hash } = e.detail; // the same hash @sealpixel/render produces
  });
</script>

<sealpixel-editor theme="auto" locale="en"></sealpixel-editor>

Open in StackBlitz

Looks like your app

CSS custom properties for every colour, radius and size. ::part() reaches buttons, nav and stage. Five themes and six languages ship with it.

  • 25

    design tokens

  • 18

    shadow parts

  • 5

    themes

  • 6

    languages

@import '@sealpixel/ui/themes/nocturne.css';

sealpixel-editor {
  --sealpixel-color-accent: #b48cff;
  --sealpixel-radius: 16px;
  --sealpixel-toolbar-size: 72px;
}

sealpixel-editor::part(export) { display: none; }
sealpixel-editor::part(tool) { border-radius: 999px; }

Open the customizer

Extend it, don’t fork it

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-rust
cd acme-spotlight && npm test   # parity: Node vs Chromium, WebKit, Firefox

Parity and fuzz testing kit included ·Plugin SDK

How it compares

Facts from the vendors’ public pages, September 2026; the C2PA row checked on 2 November 2026.

SealpixelPinturaIMG.LY CE.SDK
C2PA manifest from the edit logYes, signed with your own keyUnknown, not on their pagesUnknown, not on their pages
Source codeSource available: read, modify, redistributeClosed source, private npm registryClosed source, escrow for enterprise
Server-side rendering@sealpixel/render on Node, Deno, Bun, Lambda, no browserYes, with a browser runtimeNode.js
Byte-identical client and server outputYes, hash-verifiedNoSame engine, but not published as a guarantee
Serializable edit stateEdit log, typed and validatedimageState objectScene file
Third-party plugin SDKJS and Rust kernelsFixed setPlugin API
React, Vue, Svelte, AstroYes, plus Web ComponentYes, plus Angular, jQueryYes, plus native mobile SDKs
Price under EUR 1M revenueFree€169 per year, one productQuote only
Price per developer, per year€999€749Quote only
Money-back window30 days60 days30-day trial

Free for people. Fair for companies.

One rule: under a million in annual revenue you pay nothing. Above it, you pay per developer who touches the code.

Small

Free

under EUR 1,000,000 of annual revenue

For individuals and companies under the threshold, in production and in commercial products.

  • The whole editor, every wrapper and the render package
  • Every release
  • Community support on GitHub
Start building

Company

€999

per developer seat and year, net

From EUR 1,000,000 of annual revenue. One seat per developer who touches the code; reassignable, for every product and environment.

  • Twelve months of updates and email support
  • Every release you received stays yours, forever
  • 30-day money-back guarantee

Buying for a company? Enter your company name and VAT number in the checkout under “Add VAT number”, so the invoice carries them.

Provenance

€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.

Enterprise

On request

from 25 seats

When procurement needs more than an invoice.

  • Source code escrow
  • Custom terms and SLA
  • Purchase orders, invoicing and bank transfer
Talk to us

Seat tiers

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.

SeatsPer seat and year, net
1 to 4entry tier€999
5 to 9€899
10 to 24€799
25 and moreTalk to us

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.