## Demo: image-pipeline ### ImagePipelineDemo.svelte ```svelte

Each example starts from a single source. cachedImage(url) fetches and caches the source photo, then caches every transform below on disk — so the first request does the work and later requests (even after a restart) skip the fetch, decode, and encode. The chain mirrors Bun.Image's API; every snippet reuses this img.

Metadata

.metadata() reads dimensions and format from the header without decoding pixels:

Decoded source {meta.width} × {meta.height} · {meta.format}

Resize · fit

The same source into a 240×240 box. fit: 'fill' stretches to the exact box; fit: 'inside' preserves aspect ratio and fits within it:

fit fillfit: 'fill'
fit insidefit: 'inside'

Resample filter

filter picks the resampling kernel. To make it visible, each image is shrunk to 56px then enlarged to 240px — 'nearest' keeps hard pixels, the default 'lanczos3' interpolates smoothly:

nearest filterfilter: 'nearest'
lanczos3 filterfilter: 'lanczos3'

Rotate

.rotate(deg) turns the image clockwise in multiples of 90:

rotated 90 degreesrotate(90)
rotated 180 degreesrotate(180)
rotated 270 degreesrotate(270)

Flip · flop

.flip() mirrors vertically (about the x-axis); .flop() mirrors horizontally:

originaloriginal
flippedflip()
floppedflop()

Modulate

.modulate() adjusts brightness and saturation (1 = unchanged):

greyscalesaturation: 0
brightenedbrightness: 1.5
saturatedsaturation: 2

Output formats

The same 300px image encoded three ways, with byte sizes. Bun.Image can also encode avif() and heic(), but those go through the OS backend and are only available on macOS and Windows:

{#each formats as { label, out } (label)}
{label} {label} · {kb(out.size)}
{/each}

Indexed-palette PNG

png({'{'} palette: true }) quantizes to a ≤256-colour indexed PNG — typically several times smaller than truecolor:

truecolor pngtruecolor · {kb(pngTruecolor.size)}
palette png palette: true, colors: 32, dither: true · {kb(pngPalette.size)}

Quality

The same JPEG at two quality levels — the trade-off between artefacts and bytes:

low quality jpegquality: 20 · {kb(qLow.size)}
high quality jpegquality: 85 · {kb(qHigh.size)}

Placeholder

.placeholder() returns a ThumbHash-rendered blur as a data: URL (~400–700 bytes, no client decoder) — inline it as an instant low-quality preview before the real image loads:

ThumbHash blur placeholderplaceholder()
Full-resolution renderfull image

Progressive JPEG

jpeg({'{'} progressive: true }) encodes a progressive JPEG that paints coarse-to-fine as it downloads — visually identical once loaded, but it appears sooner over a slow connection:

progressive jpegprogressive: true · {kb(progressive.size)}
``` ### routes.ts ```ts import { Mochi } from 'mochi-framework'; import type { MochiRouteValue } from 'mochi-framework'; export const routes: Record = { '/demos/image-pipeline': Mochi.page('./src/demos/image-pipeline/ImagePipelineDemo.svelte'), }; ``` ### index.ts ```ts import { Mochi, logger } from 'mochi-framework'; import { routes } from './routes'; await Mochi.serve({ port: 3333, development: process.env.MODE === 'development', routes, }); logger.info('Server running at http://localhost:3333'); ```