Medium Pool · every medium your app could speak
$npx skills add funclosure/medium-pool

Image

A still the person hands you
messageThe person shows instead of tells.
instance Filter labUpload, transform, preview

Images are how people show an app something they cannot describe: a receipt, a whiteboard, a screenshot, a face. The medium is half about getting the file in (drop, paste, pick, capture) and half about doing something visible with it before any upload.

Everything here runs locally. A canvas gives you pixels; pixels give you filters, crops, thumbnails, and previews without a server round trip.

Reach for it when

  • the input is visual and typing it would be slower or lossy
  • people need to see the result of a change before committing
  • privacy matters and processing can stay on the device
direction
in (the person gives it)
substrate
file input, drop, paste · FileReader · 2D canvas pixels

Where it bites

  • Read files with FileReader as data URLs or decode with createImageBitmap; never trust the file extension.
  • Large photos (12–48 MP) will freeze the tab if you run getImageData on them. Downscale first.
  • EXIF orientation: phone photos may arrive rotated. Use image-orientation: from-image or createImageBitmap with imageOrientation.
  • Cross-origin images taint the canvas and getImageData throws. Same-origin or CORS headers only.
  • Support paste (Ctrl/Cmd+V) and drop, not just the file picker.

Instances of this medium

Each is one way to build it. None of them is the medium.

Filter labin the pool

Upload, transform, preview. reference/ImageLab.ts

Squooshopen source

In-browser image compression with codecs compiled to WebAssembly. The on-device processing ideal.

Cropper.jsopen source

Cropping, rotating, and scaling with a canvas result.

$npx skills add funclosure/medium-pool --skill medium-image
skills/medium-image/SKILL.md
---
name: medium-image
description: "Build image input and on-device processing on the web: drop, paste, pick, preview, filter, crop, thumbnail. Use when adding photo upload, screenshots, avatars, or visual input."
---

# Image as a medium

## What this medium does to the person
Images let a person show instead of tell. The app becomes something you can hand things to: a receipt, a whiteboard, a face. Processing on the device keeps that gesture private.

## When to reach for this medium
Choose images when the input is visual, when people must preview a change before committing, or when processing should stay on the device.

## Core browser APIs
- `<input type="file" accept="image/*">`, drag-and-drop, and paste events for getting files in
- `FileReader` / `createImageBitmap` to decode; 2D canvas `getImageData` / `putImageData` for pixel work
- `canvas.toBlob()` for exporting thumbnails and processed results

## UX rules that always apply
- Accept drop, paste, and pick. Say so in the drop zone.
- Show the file name, dimensions, and size once loaded.
- Downscale before pixel work so the tab never freezes; say when you did.
- Process locally by default and tell the user the image never leaves the browser.

## Reference instances
Each is one way to speak this medium, cited as evidence. None of them is the medium.

`reference/ImageLab.ts` — drop zone with picker fallback, generated sample image at rest, grayscale, pixelate, and threshold filters on ImageData, with parameter sliders. It uses a few layout class names from the pool's stylesheet (`row`, `btn`, `status`, `frame`); without that stylesheet the logic still runs and the controls are unstyled.

## Gotchas
- Never run getImageData on a full-resolution phone photo.
- Handle EXIF orientation or portrait photos land sideways.
- Cross-origin images taint the canvas.