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.

Single 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
Gradient — Looping Pixel Draws
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
Pixel with Alpha
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 numberX 50
PercentageX 50%
KeywordTOP | LEFT | CENTRE | RIGHT | BOTTOM

GRAPHIC — Load and Export

Load from File or URL
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER IMAGE LOAD "https://example.ocalt.com/img/photo.png" SET ?img2
A URL is downloaded into your namespace first and then loaded, so the returned ?img("path") is always a namespace path. An unreachable host is a catchable E4001; a non-2xx response is E4002.
Export Formats
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
EXPORT returns the encoded image as base64, not a file path — encoded image bytes are never valid text, so base64 is how binary travels through OcaltQL. Write it with 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

Resize
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 *)
Crop
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img CROP POSITION X 10 Y 10 WIDTH 200 HEIGHT 150 SET ?img
Rotate
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
Flip
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img FLIP HORIZONTAL SET ?img
AFTER GRAPHIC ?img FLIP VERTICAL SET ?img
Thumbnail
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img THUMBNAIL WIDTH 128 HEIGHT 128 SET ?img
THUMBNAIL gives exactly the dimensions asked for. The image is scaled to cover the box and then centre-cropped, so aspect ratio is preserved without distortion and the result is precisely 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.

Whole-Image Filters
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
Selection-Scoped Filter
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.

REPLACE — Global Color Swap
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img REPLACE "red" WITH "#ffff00" SET ?img
FILL — Local Boundary Fill
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img FILL X 10 Y 20 WITH "purple" SET ?img
GET COLOR OF — Sample a Pixel
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

Full Text Options
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

Text and Image Watermark
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
Composite an Overlay
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

Remove Background
IMAGE LOAD "/root/portrait.jpg" SET ?img
AFTER GRAPHIC ?img REMOVE BACKGROUND SET ?img

GRAPHIC — Histogram

Color 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.

A plane has no size of its own — it takes the dimensions of the image it joins. 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.
Layers are composited lazily. 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.
Creating and Compositing a Layer
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
Adding an Image onto a Layer
NEW GRAPHIC LAYER SET ?layer1
AFTER IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?layer1 ADD IMAGE ?img SET ?layer1
Layer Order, Opacity, Blend, Visibility
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
Flatten Layers into a Final Image
IMAGE LOAD "/root/img.jpg" SET ?img
AFTER GRAPHIC ?img FLATTEN SET ?final

GRAPHIC — Selections

Selection Shapes
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
Cut, Copy, Delete, Move, Paste
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
RAW IMAGE draws pixels directly by coordinate. GRAPHIC is the full editing suite — transforms, filters, text, color operations, watermarking, background removal, histograms, layers, and selections. Layers are transparent images, composited in either direction. Selections scope filters and support cut, copy, delete, move, and paste. X/Y and color inputs follow the same flexible formats everywhere they appear across the whole page.

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.

Resize on Demand and Serve It
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
Bytes are 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.
A Thumbnail Endpoint
(* /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"> *)
Drawn Pixel by Pixel, Served Straight Out
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
Nothing was uploaded and nothing was linked. The image was drawn in the request that served it, and the bytes went out as the response body — a 256×64 PNG in 351 bytes.
The response carries whatever content type the script last set. With none set it is text/plain, so emitted HTML shows as tags until the script asks for HEADER HTML. Nothing is guessed on your behalf.