Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
••
Sicherheitslücken (CVE)CVE-2026-21140 | Samsung Devices access control (EUVD-2026-91157)(02.10.2026 um 05:08 Uhr)
••••
Sichere ProgrammierungWhat Can NSL Actually Do? — A Look at Nova Signal Language(02.10.2026 um 05:14 Uhr)
••••••
Sicherheitslücken (CVE)CVE-2026-21140 | Samsung Devices access control (EUVD-2026-91157)(02.10.2026 um 05:08 Uhr)
••••
Sichere ProgrammierungWhat Can NSL Actually Do? — A Look at Nova Signal Language(02.10.2026 um 05:14 Uhr)
••••
Intelligence View
⚡ tsecurity.de Intelligence

Watermarks, overlays, and blend modes in a few lines — imgkit now has composite()

If you ship anything with user-generated or product imagery, you eventually need to put one image on top of another: a logo in the corner, a "SAMPLE" stamp…

Beitrag
0
Seite
0
↗ Quelle (dev.to)
Social ReaktionenReagiere als Erste:r — dein Feedback zählt!

If you ship anything with user-generated or product imagery, you eventually need to put one image on top of another: a logo in the corner, a "SAMPLE" stamp across a preview, a badge on a product shot, a gradient blended over a hero photo.



This usually means reaching for sharp and stitching together its composite API, or dropping down to node-canvas and doing the math by hand. The latest release of imgkit (v2.3.0) adds a dedicated composite() for exactly this — Rust-backed, async off the main thread, and the same on Node.js and Bun.






Install






bun add imgkit
# or
npm install imgkit






Prebuilt binaries ship for the common platforms, so there's no build step on install.






The hello-world: a corner watermark



The most common case — a semi-transparent logo anchored to the bottom-right corner, nudged in from the edges:




import { composite, resize } from 'imgkit';

const photo = Buffer.from(await Bun.file('photo.jpg').arrayBuffer());
const logoRaw = Buffer.from(await Bun.file('logo.png').arrayBuffer());

// Scale the logo first, then composite
const logo = await resize(logoRaw, { width: 180 });

const watermarked = await composite(photo, {
layers: [
{ input: logo, gravity: 'southEast', opacity: 0.7, offsetX: -32, offsetY: -32 },
],
output: { format: 'jpeg', jpeg: { quality: 90 } },
});

await Bun.write('watermarked.jpg', watermarked);






That's the whole API surface for the simple case. Everything else is just more layers and more options.






The mental model



composite() paints an array of layers onto a base image. The rules are small enough to keep in your head:





  • Layers paint in array order — the first layer is the bottom-most overlay, the last sits on top.


  • Placement is either gravity or absolute. Use gravity (center, north, southEast, etc.) to anchor a layer to a region, with optional offsetX / offsetY nudges. Or set left / top for pixel-precise placement — those override gravity, and off-canvas/negative values are clipped for you.


  • opacity (0.0–1.0) fades a layer; resize scales it before compositing; tile: true repeats it across the whole base.


  • blend picks the blend mode — over (default), multiply, screen, overlay, darken, lighten, or add, following the standard W3C separable blend formulas.






Recipes



Tiled "SAMPLE" / "DRAFT" stamp across the whole image:




const tiled = await composite(photo, {
layers: [{ input: stamp, tile: true, opacity: 0.15 }],
});






Blend a gradient or texture over a photo:




const blended = await composite(photo, {
layers: [{ input: gradient, blend: 'multiply', opacity: 0.6 }],
});






Stack several layers — a product on a background, a badge top-right, a faded logo bottom-left:




const out = await composite(background, {
layers: [
{ input: product, gravity: 'center' },
{ input: badge, gravity: 'northEast', resize: { width: 80 } },
{ input: logo, gravity: 'southWest', opacity: 0.8 },
],
output: { format: 'webp', webp: { quality: 90 } },
});









A couple of things worth knowing




  • The base is decoded to RGBA, so the result preserves transparency by default — composite() returns PNG unless you set output. Choosing a JPEG output flattens any alpha (it's dropped, not matted), which is usually what you want for a final watermarked photo.

  • For lots of layers or large canvases, the async composite() runs off the main thread and can be cancelled with an AbortSignal or a timeoutMs via AsyncOptions. There's a compositeSync() if you'd rather stay synchronous.

  • Fully transparent source pixels and fully opaque over paints take fast paths, so the common watermark case stays cheap.






Why I built it this way



imgkit's whole point is to keep the fast path in Rust (via napi-rs) while exposing an API that feels native to JS — one function, plain options objects, Buffers in and out, identical behavior on Node and Bun. composite() follows that: no canvas, no manual pixel loops, no separate paths for "watermark" vs "blend" vs "tile." They're all just layers.



If you want the full option reference and more recipes, the docs are here:





If you try it, I'd love to hear what you're compositing — and issues/stars on GitHub are always welcome.

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Watermarks, overlays, and blend modes in a few lines — imgkit now has composite()

Thematisch verwandte Begriffe: Watermarks, overlays, blend, modes · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

💬 Kommentare werden geladen…
Zum Aktualisieren ziehen
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag