Images from Code
A picture you load in a pictures.js scene can be changed in code: cut out its subject, resize it, crop, rotate, trace it into vector shapes, read what is in it. Each change gives you a new picture in memory, ready to draw, to give to a Sprite, or to save.
const img = loadImage("shoe.jpg") // a picture beside this scene
const cut = await img.removeBackground() // the shoe, background transparent (Apple devices only)
const small = await cut.resize({ width: 800 }) // one step after another
await small.save("shoe cutout.png") // optional: write it as a file
function draw() {
background("white")
ctx.drawImage(small, 40, 40)
}
These are the methods a Python scene has in Images from Code for Pyctures, with JavaScript's spelling: remove_background is removeBackground, and the options go in one pair of braces, { width: 800 }.
Where each method runs
The work is done by the Circuitry app, so these methods run in scenes inside Circuitry. They are not part of the pictures.js library on its own, and not of a scene exported as a web page: there a picture still loads and draws, and calling one of these stops the scene with a message that says they work inside Circuitry (Use Your Scenes Outside Circuitry).
Inside Circuitry, what a method needs depends on the method:
- Any device Web, Mac, Windows, Linux, iPhone, iPad and Android:
resize,crop,rotate,flip,convert,trace,threshold,edgeDetect,cannyEdges,findContours,detectShapes,watermark,stripMetadata,faviconSet,appIconSet,save. - Apple devices The Circuitry app on a Mac, iPhone or iPad, or a browser connected to Circuit running on a Mac:
removeBackground,enhance,sepia,mono,noir,vintage,blur,sharpen,vibrance, and the photo toolsclassify,findFaces,readCodes,photoDetails,hideFaces,scanDocument,smartCrop,straighten.
The photo tools on a Mac need the current version of the Circuitry desktop app, or of Circuit when a browser, phone or tablet is connected to one. With an older one they are not offered: available() leaves them out, and calling one stops with a message that says what it needs.
The Apple-only ones use the picture tools built into Apple's systems (Vision and Core Image), which work on the device itself. They are not Apple Intelligence, and nothing is sent to a server.
System versions. iOS 17 / macOS 14 or later removeBackground needs iOS 17, iPadOS 17 or macOS 14 or later. Every other method here runs on any system version the Circuitry app itself runs on.
A method called where it cannot run stops with a message that says where it does. Every method lists each one with where it runs, and await img.available() gives the names this device can run.
How it works
loadImage("name.png")loads a picture from the folder your scene is saved in. You can draw it straight away, as before.- A picture from a web address can be drawn, but most cannot be changed: a browser lets a page read a picture's pixels only when the picture's own site allows it. Save the picture beside your scene and load it from there.
- Every method is awaited:
await img.resize({ width: 800 }). Put the line at the top of the scene, or inside anasync functionsuch asasync function setup(). Do not call one indraw(): change the picture once, and draw the result every frame. - A method never changes the picture you called it on. It gives a new one, so you can keep both, or chain:
await (await img.removeBackground()).resize({ width: 800 }). - The new picture has finished loading when you get it, so
small.widthandsmall.heightare right at once. It is drawn like any other:ctx.drawImage(small, x, y),picture(small, x, y, size),new Sprite(small, x, y, size). - The work happens in the app, not in your scene, and a scene cannot reach the app: it is handed the new picture and nothing else (what a scene can reach).
- Results are remembered. Running the scene again, or recording a video of it, reuses the results instead of redoing the work, as long as the picture and the options are the same.
- Nothing is written anywhere unless you call
save().
Remove a background
const product = loadImage("bottle.jpg")
const cutout = await product.removeBackground()
function draw() {
background("#f4f1ea")
picture(cutout, width / 2, height / 2, height * 0.8)
}
removeBackground() finds the main subject (a product, a person, a pet) and makes everything else transparent. It uses Apple's own on-device subject lifting, so it needs the Circuitry app on a Mac (macOS 14 or later), iPhone or iPad (iOS / iPadOS 17 or later), or a browser connected to Circuit running on a Mac. Anywhere else the line stops with: removeBackground() needs an Apple device. On iPhone and iPad it needs a real device; it does not run in the iOS Simulator.
Resize a picture
const hero = await img.resize({ width: 1280 }) // height follows the shape
const thumb = await img.resize({ percent: 25 })
const square = await img.resize({ width: 1080, height: 1080 }) // whole picture, centred
const cover = await img.resize({ width: 1080, height: 1080, fit: "cover" }) // fill the square, trim the edges
Give width, height or percent. With both width and height, fit says how the picture fills the box: "contain" (the default: the whole picture, with clear margins), "cover" (fills the box and trims the edges) or "stretch". Big reductions are made in halves before the final size, so detail and text stay sharp. Transparency is kept.
Trace a picture into shapes
const logo = loadImage("logo.png")
const vec = await logo.trace({ colors: 6, detail: "medium" })
await vec.save("logo.svg")
function draw() {
background("white")
picture(vec, width / 2, height / 2, 300) // sharp at any size
}
trace() turns a picture into flat-colour vector shapes, the same tracing 2Vec uses. colors is how many colours to keep; detail is "low" (smoother, fewer points), "medium" or "high". A logo or an illustration with clear areas of colour traces best; a photo becomes a poster-like picture. What you get is a picture you can draw at any size, with vec.svg (its SVG text) and vec.shapes (how many shapes it has). It saves as .svg. It has no other methods: trace last.
Data from a picture
Some methods read a picture instead of changing it, and give plain JavaScript data. classify, findFaces, readCodes and photoDetails need an Apple device (the Circuitry app on a Mac, iPhone or iPad, or Circuit running on a Mac); findContours and detectShapes run everywhere.
const info = await img.classify({ maxLabels: 5 })
for (const item of info.labels) { // e.g. { label: "shoe", confidence: 0.93 }
console.log(item.label, item.confidence)
}
const codes = await img.readCodes({ overlay: false })
for (const code of codes.codes) { // each has symbology and payload
console.log(code.payload)
}
Methods that can draw what they found (findFaces, readCodes, findContours, detectShapes) give { found, overlay } when overlay is true (their default): found is the data and overlay a picture with the finds drawn on. Pass { overlay: false } for the data alone.
Saving
await pic.save("name.png") writes the picture as a file (.png, .jpg or .webp; the format follows the name) and gives its path. It replaces a file of that name. A traced picture saves as .svg. faviconSet() and appIconSet() make a set of files: icons.names lists them, and await icons.save("icons") writes them into a folder.
A saved picture goes where the scene keeps its other files: beside the scene in the Circuitry app on a phone, a tablet or a computer, and in the browser when Circuitry runs in a web browser. Where files and databases are kept has the whole table, and the same rules about names hold: a scene writes inside its own folder, and never a file that is run.
What this device can run
console.log(await img.available()) // the method names this device can run
Most methods run everywhere. Apple's effects and the photo tools need an Apple device: the Mac app, iPhone or iPad, or Circuit on a Mac. On a Mac the photo tools need the updated desktop app or Circuit, so the list is the way to know what this one has.
Mistakes say what is wrong
- An option the method does not take: resize() has no option "wdith". It takes width, height, percent, fit.
- A method no picture has: A picture has no removeBackgroud().
- A device that cannot run it: removeBackground() needs an Apple device: the Circuitry app on a Mac, iPhone or iPad, or Circuit running on a Mac.
Each stops the scene on its own line, like any other mistake (Finding Mistakes). To carry on without the picture, catch it: const cut = await img.removeBackground().catch(() => img).
Every method
Every picture from loadImage has these methods. Each is awaited (await img.resize({ width: 800 })) and takes its options in one pair of braces. An option you leave out takes the value shown; one shown with no value is simply not used. await img.run("name", options) runs any of them by name. The Python spellings (remove_background, max_labels) work too.
| Method | What it does | Gives | Runs on |
|---|---|---|---|
removeBackground() | Cut out the main subject; everything else becomes transparent. | a new picture | Apple devicesiOS 17 / macOS 14 or later |
resize({ width, height, percent, fit: "contain" }) | Scale it. Give width, height (the other follows the shape) or percent. width: px; height: px; percent: e.g. 50 for half size; fit: with both width and height: "contain" (whole picture), "cover" (fill and crop) or "stretch". | a new picture | Any device |
enhance() | Improve exposure, colour and contrast automatically. | a new picture | Apple devices |
sepia({ intensity: 0.8 }) | Warm brown old-photo tone. intensity: 0 to 1. | a new picture | Apple devices |
mono() | Black and white. | a new picture | Apple devices |
noir() | High-contrast black and white. | a new picture | Apple devices |
vintage() | Faded, warm film look. | a new picture | Apple devices |
blur({ radius: 10 }) | Soften the whole picture. radius: how far to blur, px. | a new picture | Apple devices |
sharpen({ radius: 2.5, intensity: 0.5 }) | Crisper edges. radius: px; intensity: 0 to 1. | a new picture | Apple devices |
vibrance({ amount: 1 }) | Richer colours, gently. amount: -1 to 1. | a new picture | Apple devices |
rotate({ angle: 90, background: "#00000000" }) | Rotate by 90/180/270 or any arbitrary angle. angle: degrees, clockwise; background: colour for the corners a turn uncovers. | a new picture | Any device |
flip({ axis: "horizontal" }) | Mirror horizontally, vertically, or both. axis: "horizontal", "vertical" or "both". | a new picture | Any device |
crop({ x: 0, y: 0, width: 256, height: 256 }) | Cut a rectangular region from an image. x: left edge, px; y: top edge, px; width: px; height: px. | a new picture | Any device |
convert({ format: "webp", quality: 0.9 }) | Re-encode as PNG / JPG / WebP / AVIF / HEIC. format: "png", "jpg", "webp", "avif" or "heic"; quality: 0 to 1, for lossy formats. | a new picture | Any device |
stripMetadata({ keepOrientation: true }) | Remove EXIF / GPS / camera info by re-encoding. keepOrientation: keep the picture upright. | a new picture | Any device |
edgeDetect({ kernel: "sobel", threshold: 80 }) | Sobel / Prewitt / Laplacian edge map. kernel: "sobel", "prewitt" or "laplacian"; threshold: 0 to 255. | a new picture | Any device |
watermark({ text: "Sample", anchor: "bottom-right", opacity: 0.75, fontSize: 28, color: "#ffffff" }) | Write text over the picture. text: the words; anchor: "bottom-right", "top-left", "center"…; opacity: 0 to 1; fontSize: px; color: hex colour. | a new picture | Any device |
cannyEdges({ low: 50, high: 150 }) | Hysteresis edge detector — cleaner, connected edges vs the kernel filter. low: lower edge threshold; high: upper edge threshold. | a new picture | Any device |
threshold({ mode: "otsu", value: 127, invert: false }) | Binarize via fixed value, Otsu (auto), or local adaptive thresholding. mode: "otsu" (automatic), "binary" or "adaptive"; value: 0 to 255, for "binary"; invert: swap black and white. | a new picture | Any device |
findContours({ low: 50, high: 150, minArea: 64, overlay: true }) | Detect blobs/shapes; returns exact count + per-region area, bbox, centroid. low: lower edge threshold; high: upper edge threshold; minArea: ignore shapes smaller than this, px²; overlay: also return a picture with the finds drawn on. | data | Any device |
detectShapes({ minArea: 64, epsilon: 0.04, overlay: true }) | Classify each contour as triangle / rectangle / square / circle / polygon. minArea: ignore shapes smaller than this, px²; epsilon: corner tolerance, share of the outline; overlay: also return a picture with the finds drawn on. | data | Any device |
findFaces({ overlay: true }) | Count the faces in a photo and return where each one is. overlay: also return a picture with the faces marked. | data | Apple devices |
readCodes({ overlay: true }) | Read every QR code and barcode in the picture. overlay: also return a picture with the codes marked. | data | Apple devices |
classify({ maxLabels: 10, minConfidence: 0.1 }) | Name what is in the picture (dog, beach, food…) with a confidence for each label. maxLabels: most labels to return; minConfidence: drop labels below this, 0 to 1. | data | Apple devices |
photoDetails() | Date taken, location, camera and exposure, as data. | data | Apple devices |
hideFaces({ style: "blur" }) | Blur, pixelate or black out every face in the photo. style: "blur", "pixelate" or "solid". | a new picture | Apple devices |
scanDocument({ enhance: true }) | Find the page in a photo and flatten it to a straight-on scan. enhance: even out the lighting like a scanner. | a new picture | Apple devices |
smartCrop({ aspect: "1:1" }) | Crop to the part of the photo that draws the eye. aspect: "1:1", "4:5", "3:2", "16:9", "9:16" or "tight". | a new picture | Apple devices |
straighten() | Level a tilted horizon. | a new picture | Apple devices |
faviconSet({ includeIco: true, includeManifest: true }) | Generate web favicons: 16/32/48/192/512 PNGs + apple-touch + favicon.ico + manifest icons. includeIco: add favicon.ico; includeManifest: add the web manifest icons. | a set of files | Any device |
appIconSet({ platform: "ios" }) | Generate iOS .appiconset and/or Android mipmap density buckets with Contents.json. platform: "ios", "android" or "both". | a set of files | Any device |
trace({ colors: 16, detail: "medium" }) | Turn it into flat-colour vector shapes (as 2Vec does): a picture you can draw, with its SVG text in .svg, that saves as .svg. colors: how many colours to keep; detail: "low" (smoother, fewer points), "medium" or "high". | a traced picture | Any device |
save(name) | Write the picture where the scene keeps its files (.png, .jpg or .webp; a traced picture as .svg; a set of files as a folder). Gives the path. | the path | Any device |
run(name, options) | Run any method above by its name. | as that method | as that method |
available() | The names of the methods this device can run. | a list | Any device |
Related
- Images from Code for Pyctures: the same methods in Python, and in Pymations.
- Files and Databases: the other files a scene writes, and where they are kept.
- Drawing in 2D: loading and drawing pictures.
- From Python: bringing a Pyctures scene across.