Graphics & Image Processing
RAW IMAGE creates images at the pixel level — draw directly by coordinate, entirely server-side. GRAPHIC is a full raster editing suite — load, transform, filter, composite, add text, manage layers, and work with selections.
RAW IMAGE
NEW RAW IMAGE WIDTH w HEIGHT h SET ?var AS OPEN ... CLOSE creates a blank image of the given dimensions and executes the body, which draws individual pixels with PIXEL.
NEW RAW IMAGE WIDTH 100 HEIGHT 100 SET ?img AS
OPEN
PIXEL X 50 Y 50 AS "255,0,0"
CLOSE
AFTER EMIT ?img
NEW RAW IMAGE WIDTH 256 HEIGHT 1 SET ?img AS
OPEN
CALCULATE 0 SET ?x
AFTER WHILE ?x IS LESS THAN 256
OPEN
PIXEL X ?x Y 0 AS ?x & ",0,0"
AFTER CALCULATE ?x + 1 SET ?x
CLOSE
CLOSE
NEW RAW IMAGE WIDTH 50 HEIGHT 50 SET ?img AS
OPEN
PIXEL X 25 Y 25 AS "0,0,255,0.5"
CLOSE
(* Alpha is the 4th value, a 0–1 decimal fraction *)
Color Input Formats
Every verb that accepts a color — PIXEL, REPLACE, FILL, TEXT COLOR, and others — accepts any of the following forms:
| Format | Example |
|---|---|
| Named color | "red" |
| RGB | "255,0,0" |
| RGBA — alpha as 0–1 decimal | "255,0,0,0.5" |
Hex, 6-character, with or without # | "#ff0000" / "ff0000" |
Hex, 3-character, with or without # | "#f00" / "f00" |
X and Y Positional Values
Every X/Y coordinate, across every verb, accepts a raw pixel number, a percentage, or a positional keyword.
| Form | Example |
|---|---|
| Raw pixel number | X 50 |
| Percentage | X 50% |
| Keyword | TOP | LEFT | CENTRE | RIGHT | BOTTOM |
GRAPHIC — Load and Export
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER IMAGE LOAD "https://example.ocalt.com/img/photo.png" SET ?img2
?img("path") is always a namespace path. An unreachable host is a catchable E4001; a non-2xx response is E4002.IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img EXPORT AS "png" SET ?bytes
AFTER GRAPHIC ?img EXPORT AS "jpg" SET ?bytes
AFTER GRAPHIC ?img EXPORT AS "bmp" SET ?bytes
AFTER GRAPHIC ?img EXPORT AS "webp" SET ?bytes
AFTER GRAPHIC ?img EXPORT AS "gif" SET ?bytes
AFTER FILE WRITE "/root/output.png" CONTENT ?bytes BASE64
FILE WRITE "path" CONTENT ?bytes BASE64; the plain FILE WRITE ?bytes TO "path" form would store the base64 as literal text. The same value can be handed to anything else that accepts base64 content.GRAPHIC — Transform
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img RESIZE WIDTH 400 HEIGHT 300 SET ?img
AFTER GRAPHIC ?img RESIZE WIDTH 400 SET ?img
(* Height auto-scaled, preserving aspect ratio, when HEIGHT is omitted *)
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img CROP POSITION X 10 Y 10 WIDTH 200 HEIGHT 150 SET ?img
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img ROTATE 90 SET ?img
AFTER GRAPHIC ?img ROTATE 180 SET ?img
AFTER GRAPHIC ?img ROTATE 270 SET ?img
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img FLIP HORIZONTAL SET ?img
AFTER GRAPHIC ?img FLIP VERTICAL SET ?img
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img THUMBNAIL WIDTH 128 HEIGHT 128 SET ?img
WIDTH × HEIGHT. That is the difference from RESIZE, which preserves the whole frame and auto-scales the axis you leave out.GRAPHIC — Filters
A bare FILTER applies to the entire image. Passing a selection scopes the filter to that region only.
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img FILTER AS GREYSCALE SET ?img
AFTER GRAPHIC ?img FILTER AS SEPIA SET ?img
AFTER GRAPHIC ?img FILTER AS BLUR SET ?img
AFTER GRAPHIC ?img FILTER AS SHARPEN SET ?img
AFTER GRAPHIC ?img FILTER AS INVERT SET ?img
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img SELECT RECTANGLE X 10 Y 10 WIDTH 200 HEIGHT 150 SET ?sel
AFTER GRAPHIC ?img FILTER ?sel AS GREYSCALE SET ?img
(* Only the selected region is filtered *)
GRAPHIC — Color Operations
REPLACE swaps every pixel of a given color anywhere in the image — global. FILL is a boundary flood-fill starting at a coordinate — local. GET COLOR OF samples the color at a coordinate into a variable.
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img REPLACE "red" WITH "#ffff00" SET ?img
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img FILL X 10 Y 20 WITH "purple" SET ?img
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img GET COLOR OF X 10 Y 20 SET ?color
AFTER GRAPHIC ?img REPLACE ?color WITH "purple" SET ?img
GET COLOR OF returns one of the colour forms listed above — "r,g,b" for an opaque pixel, "r,g,b,a" where the pixel carries transparency. It can therefore be fed straight back into REPLACE, FILL, SELECT COLOR, or any other verb that takes a colour.GRAPHIC — Text
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img TEXT "Hello World" POSITION X CENTRE Y BOTTOM FONT "Arial" SIZE 24 COLOR "black"
AND WEIGHT "bold" OUTLINE "2px black" SHADOW "2,2,black" BACKGROUND "white" SET ?img
GRAPHIC — Watermark and Composite
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img WATERMARK WITH "© Ocalt" SET ?img
AFTER IMAGE LOAD "/root/assets/logo.png" SET ?logo
AFTER GRAPHIC ?img WATERMARK WITH ?logo SET ?img
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER IMAGE LOAD "/root/overlay.png" SET ?overlay
AFTER GRAPHIC ?img COMPOSITE WITH ?overlay AT X 20 Y 20 SET ?img
GRAPHIC — Background Removal
IMAGE LOAD "/root/portrait.jpg" SET ?img
AFTER GRAPHIC ?img REMOVE BACKGROUND SET ?img
GRAPHIC — Histogram
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img HISTOGRAM SET ?data
(* Returns {r:[...], g:[...], b:[...]} *)
GRAPHIC — Layers
A layer is a plane — a transparent image. Layers are created independently, then composited onto an image, or an image composited onto a layer, depending on the direction needed.
NEW GRAPHIC LAYER creates a layer with no canvas at all, so it can be created before the image it will eventually cover. ADD LAYER gives it the dimensions of the image it is added to; ADD IMAGE gives it the dimensions of the image added onto it. FILL and OPACITY applied to a layer before it joins anything are remembered and applied across the whole plane at that moment — which is why FILL X TOP Y LEFT WITH "blue" on a fresh layer colours the entire plane, not one pixel. Any other pixel operation on a layer that has not joined an image yet is a catchable E5006: there are no pixels to work on.ADD LAYER records the plane and its stacking state rather than flattening immediately, so ORDER ABOVE, ORDER BELOW, OPACITY, BLEND, HIDE, and SHOW all keep working afterwards. The stack is resolved — sorted by order, hidden planes skipped, each composited with its own blend mode — the moment anything needs actual pixels: FLATTEN, EXPORT, GET COLOR OF, a filter, a transform, or a save.NEW GRAPHIC LAYER SET ?layer1
AFTER GRAPHIC ?layer1 FILL X TOP Y LEFT WITH "blue" SET ?layer1
AFTER IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img ADD LAYER ?layer1 SET ?img
NEW GRAPHIC LAYER SET ?layer1
AFTER IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?layer1 ADD IMAGE ?img SET ?layer1
NEW GRAPHIC LAYER SET ?layer1
AFTER NEW GRAPHIC LAYER SET ?layer2
AFTER GRAPHIC ?layer2 ORDER ABOVE ?layer1 SET ?layer2
AFTER GRAPHIC ?layer2 ORDER BELOW ?layer1 SET ?layer2
AFTER GRAPHIC ?layer2 OPACITY 50 SET ?layer2
AFTER GRAPHIC ?layer2 BLEND "multiply" SET ?layer2
AFTER GRAPHIC ?layer2 BLEND "screen" SET ?layer2
AFTER GRAPHIC ?layer2 BLEND "overlay" SET ?layer2
AFTER GRAPHIC ?layer2 HIDE SET ?layer2
AFTER GRAPHIC ?layer2 SHOW SET ?layer2
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img FLATTEN SET ?final
GRAPHIC — Selections
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img SELECT RECTANGLE X 10 Y 10 WIDTH 200 HEIGHT 150 SET ?sel
AFTER GRAPHIC ?img SELECT ELLIPSE X CENTRE Y CENTRE WIDTH 100 HEIGHT 100 SET ?sel2
AFTER GRAPHIC ?img SELECT LASSO POINTS "10,10 50,10 50,50 10,50" SET ?sel3
AFTER GRAPHIC ?img GET COLOR OF X 10 Y 20 SET ?color
AFTER GRAPHIC ?img SELECT COLOR ?color TOLERANCE 20 SET ?sel4
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img SELECT RECTANGLE X 10 Y 10 WIDTH 200 HEIGHT 150 SET ?sel
AFTER GRAPHIC ?img CUT ?sel TO ?clip SET ?img
AFTER GRAPHIC ?img COPY ?sel TO ?clip2
AFTER GRAPHIC ?img DELETE ?sel SET ?img
AFTER GRAPHIC ?img MOVE ?sel X 20 Y 20 SET ?img
AFTER GRAPHIC ?img PASTE ?clip AT X 100 Y 100 SET ?img
A Script That Is an Image
An image does not have to be written somewhere and linked to. Set a content type, emit the bytes, and the response is the image — the same thing a PHP script does with GD. Point an <img src> at the script and it renders.
IMAGE LOAD "/root/photo.jpg" SET ?img
AFTER GRAPHIC ?img RESIZE WIDTH 400 SET ?small
AFTER HEADER "Content-Type" AS "image/jpeg"
AFTER FILE READ ?small("path") SET ?bytes
AFTER EMIT ?bytes
FILE READ hands back a real bytes value for a binary file, and EMIT writes those bytes to the response untouched — no encoding, no wrapping, no base64. You reach for base64 only when something on the far side cannot carry binary, and then you ask for it: ENCODE ?bytes AS BASE64.(* /root/sites/cdn/thumb.oql — reachable at cdn.ocalt.site/thumb *)
VARIABLE !GET("id") SET ?id
AFTER IF ?id IS EMPTY
OPEN
STATUS 404
AFTER EMIT "no id"
AFTER EXIT
CLOSE
AFTER IMAGE LOAD "/root/uploads/" & ?id & ".jpg" SET ?img
AFTER GRAPHIC ?img RESIZE WIDTH 240 SET ?thumb
AFTER HEADER "Content-Type" AS "image/jpeg"
AFTER HEADER CACHE
AFTER FILE READ ?thumb("path") SET ?bytes
AFTER EMIT ?bytes
(* <img src="https://cdn.ocalt.site/thumb?id=42"> *)
NEW RAW IMAGE WIDTH 256 HEIGHT 64 SET ?img AS
OPEN
CALCULATE 0 SET ?x
AFTER WHILE ?x IS LESS THAN 256
OPEN
PIXEL X ?x Y 32 AS ?x & ",0,120"
AFTER CALCULATE ?x + 1 SET ?x
CLOSE
CLOSE
AFTER HEADER "Content-Type" AS "image/png"
AFTER FILE READ ?img("path") SET ?bytes
AFTER EMIT ?bytes
text/plain, so emitted HTML shows as tags until the script asks for HEADER HTML. Nothing is guessed on your behalf.