#### <sup>[Ollin](../index.md) → [Documentation](index.md) → `QuickReference`</sup>

---

## Ollin quick reference

This page is the whole shape of Ollin in one read. It covers how a sketch runs, what the calls are called, their units, the command line, and the mistakes that fail with no error. Each part links to the page with the rest. It is written for anybody writing a sketch, and for an assistant helping somebody write one. Read it once, then look details up with the commands under [Looking something up](#lookup).

New to Swift? The [Swift quick reference](swift.md) does the same for the language. Coming from p5.js or Processing, [Appendix C](../guide/c-comingfromp5.md) of the Guide translates the API you know, and [Appendix E](../guide/e-comingfromopenframeworksandopenrndr.md) does it for openFrameworks and OPENRNDR.

### Contents

- [A sketch](#sketch)
- [The clock and randomness](#clock)
- [The calls, by area](#calls)
- [Names you might reach for](#names)
- [Units and conventions](#units)
- [Parameters](#params)
- [The command line](#cli)
- [Looking something up](#lookup)
- [Things that fail quietly](#quiet)
- [Checking your work](#checking)

<a name="sketch"></a>

### A sketch

A sketch is a class that builds on `Sketch`. `setup()` runs once, and `draw()` runs every frame at the display's rate, so anything that reads `time` moves.

```swift
import Ollin

@main
final class Rings: Sketch {
    override var canvasSize: CanvasSize { .vertical1080 }
    override var loopDuration: Double? { 6 }

    @Param(4...40) var count = 12
    @Param var ink: Color = .black

    override func setup() {
        seed(7)
    }

    override func draw() {
        background(.white)
        noFill()
        stroke(ink)
        strokeWeight(2 * scale)
        let t = loopProgress(over: 6)
        for i in 0..<count {
            let wobble = sin((t + Double(i) / Double(count)) * .tau) * 12
            drawCircle(width / 2, height / 2, (40 + Double(i) * 30 + wobble) * scale)
        }
    }
}
```

- **Configuration is overridden, not called.** `canvasSize`, `windowMode`, `loopDuration`, `title`, and `colorOutput` are properties a sketch overrides.
- **The hooks** are `setup()`, `draw()`, `mousePressed()`, `mouseReleased()`, `mouseScrolled()`, `keyPressed()`, `keyReleased()`, `filesDropped()`, and `reloaded()` (after a hot swap). Mouse movement has no hook; read `mouseX`, `mouseY`, and `mouseIsPressed` in `draw()`.
- **Draw in `draw()`, not in `setup()`.** The frame's geometry is emptied before the first `draw()`, and per-frame state (the camera, lights, shadows) resets each frame.
- **`draw*` calls emit shapes**; `fill`, `stroke`, and `background` set state; `translate`, `rotate`, and `scale` move the coordinates. `withState { }` keeps a change of state or transform inside its block.
- **Keep state in properties.** A `Feedback`, an `Accumulator`, a loaded `Image`, or a `Mesh` is made once, in `setup()` or as a stored property, and used every frame.
- **Loading a file throws.** `loadImage`, `loadMesh`, `loadScene`, `loadSVG`, `loadPalette`, `loadTable`, `loadJSON`, and the initializers under them throw a [`FileError`](core/sketch.md#fileerror) (`missing` or `unreadable`, with the path and a sentence). `setup()` may throw, so a bare `try` stops the sketch with that sentence (on stderr, and in a host's error strip, with the window held open), or write `try?` to carry on with `nil`.

[Sketch](core/sketch.md) covers the lifecycle and [Canvas](core/canvas.md) the canvas and window.

<a name="clock"></a>

### The clock and randomness

- **`time`** is seconds since `setup()`. In a window it adds up the real frame steps, each capped at 0.25 s, so a stall pauses the clock rather than jumping it. In an export it is exactly the frame number over the frame rate.
- **`frameCount`** is 1 during the first `draw()`. **`deltaTime`** is the step since the last frame: 0 on the first frame in a window, exactly `1 / fps` in an export.
- **`loopProgress(over:phase:)`** runs from 0 up to 1 over the given seconds and wraps. **`pingPong(over:phase:)`** goes 0 to 1 and back.
- **`loopDuration`** declares the length of one lap. It does not change `time`. It tells `--export-loop` how many frames make exactly one lap, and makes a web export loop.
- **`noLoop()`** stops the window's timer after the current frame. Exports hold that frame too, so a frame asked for past it is the frame it stopped on, drawn once. **`redraw()`** draws one more frame of a stopped sketch, from an input hook.
- **`isExporting`** is true through the whole of an export, `setup()` included, and false in a window: work spread over live frames is done at once when it is true.
- **`random()`** returns 0 up to but not including 1, `random(a, b)` a up to b, and `randomGaussian()` has mean 0 and deviation 1.
- **`seed(n)`** fixes both `random` and `noise` and sets `variation`. `randomSeed` and `noiseSeed` fix one each. A sketch that never seeds gets a different variation at every launch.

[Animation](helpers/animation.md), [Random](generators/random.md), and [Determinism](concepts/determinism.md) have the rest.

<a name="calls"></a>

### The calls, by area

The names a sketch reaches for first in each area. `ollin api <name>` gives any of them in full.

| Area | Calls | Page |
| --- | --- | --- |
| Canvas and frame | `background`, `width`, `height`, `center`, `scale`, `noClear`, `makeAccumulator`, `withAccumulator`, `noLoop`, `redraw`, `isExporting` | [Sketch](core/sketch.md), [Accumulation](drawing/accumulation.md) |
| 2D shapes | `drawCircle`, `drawEllipse`, `drawRect`, `drawLine`, `drawPoint`, `drawTriangle`, `drawArc`, `drawPolygon`, `drawNgon`, `drawStar`, `drawRing`, `drawBezier`, `drawArrow` | [Drawing](drawing/drawing.md) |
| Style | `fill`, `noFill`, `stroke`, `noStroke`, `strokeWeight`, `strokeCap`, `strokeJoin`, `strokeDash`, `strokeAlign`, `blendMode`, `hollow`, `solid` | [Drawing](drawing/drawing.md) |
| Transforms | `translate`, `rotate`, `scale`, `withState`, `mirrored`, `repeated`, `withClip` | [Drawing](drawing/drawing.md) |
| Outlines | `drawShape`, `drawCurve`, `drawPolyline`, `Path`, `Shape`, `Contour`, `Vector2`, `drawSVG` | [Geometry](drawing/geometry.md) |
| Text | `drawText`, `textFont`, `textSize`, `textAlign`, `textWidth`, `textToShapes` | [Text](drawing/text.md) |
| Images | `loadImage`, `drawImage`, `Image`, `tint`, `noTint` | [Images](drawing/images.md) |
| Color | `Color`, `Palette`, `Ramp`, `Gradient`, `CosinePalette`, `Colormap`, `OKLCH` | [Color](drawing/color.md) |
| Math and noise | `map`, `lerp`, `clamp`, `smoothstep`, `polar`, `spherical`, `random`, `noise`, `signedNoise`, `fbm`, `curlNoise` | [Math](helpers/math.md), [Noise](generators/noise.md) |
| Motion | `loopProgress`, `pingPong`, `Easing`, `Eased`, `Sprung`, `every`, `after` | [Animation](helpers/animation.md) |
| Layers and effects | `makeRenderTarget`, `withTarget`, `filtered`, `Filter`, `combined`, `postProcess`, `makeFeedback`, `withFeedback`, `generate` | [Effects](drawing/effects.md) |
| Your own shaders | `Shader`, `Filter`, `Generator`, `Visual`, `drawVisual`, `compute` | [Shaders](shaders/shaders.md) |
| 3D | `camera`, `Camera3D`, `cameraControl`, `drawBox`, `drawSphere`, `drawMesh`, `Mesh`, `material`, `Material`, `directionalLight`, `pointLight`, `environment`, `castShadows`, `temporalAntialiasing`, `motionBlur` | [3D](3d/3d.md) |
| Input | `mouseX`, `mouseY`, `mouse`, `mouseIsPressed`, `key`, `keyIsPressed`, `isKeyDown`, `droppedFiles`, `chooseFiles`, `pointerShape`, `hidePointer` | [Input](helpers/input.md) |
| Parameters | `Param`, `ParamGroup`, `Saved`, `variation` | [Parameters](helpers/parameters.md) |
| Techniques | `poissonDisk`, `voronoi`, `delaunay`, `flowField`, `drawLSystem`, `drawWFC`, `packCircles`, `drawTruchet`, `stipple` | [Generators](generators/index.md) |

<a name="names"></a>

### Names you might reach for

Some names from other frameworks mean something else here, or nothing. `ollin api` catches the near misses and lists the sketch calls spelled like the name you typed. It cannot catch these:

| You might type | In Ollin |
| --- | --- |
| `circle(x, y, d)` | `drawCircle(x, y, radius)`, which takes a radius rather than a diameter |
| `strokeWidth`, `lineWidth` | `strokeWeight` |
| `push()` and `pop()` | `withState { }` |
| `createCanvas(w, h)` | `override var canvasSize: CanvasSize { .size(w, h) }` |
| `saveFrame` | `--export` or `--export-sequence` on the command line, or `OllinApp.image(of:frame:)` in code; `copyFrame()` puts the frame on the clipboard |
| `cursor(CROSS)` and `noCursor()` | `pointerShape(.crosshair)` and `hidePointer()`, with `showPointer()` to bring it back |
| `selectInput(prompt, callback)` | `chooseFiles(withExtensions:)`, with the pick read from `chosenFiles()` in `filesChosen()` |
| `millis()` | `time`, in seconds |
| `radians(45)` | `.degrees(45)`, which converts degrees to the radians every drawing call takes |
| `beginShape()` and `endShape()` | `drawShape { }` with a `Path`, or `drawPolyline` for points |
| a Poisson fill, a Laplace or membrane fill of the space between marks | [`.diffuse()`](drawing/effects.md#generate) filtered over a layer holding the marks. `poissonDisk` is something else: it scatters points. |

<a name="units"></a>

### Units and conventions

| What | Convention |
| --- | --- |
| 2D coordinates | The origin is the top-left corner and y points down. `width` and `height` are the canvas in pixels. |
| `scale` (the property) | `min(width, height) / 1000`. Write sizes for a 1000-pixel canvas and multiply by it, and the sketch holds at any canvas size. It is separate from the `scale(_:)` transform. |
| 2D angles | Radians, 0 along +x, turning clockwise on screen because y points down. `rotate`, `drawArc`, `polar`, and `Vector2(angle:length:)` all work this way. |
| Degrees | `.degrees(45)` and `.turns(0.125)` convert to radians. The few calls that take degrees say so: an `LSystem` angle, `Ocean.windDirection`, the `Physarum` angles, and the latitude, longitude, and sun position of a `Place`. |
| The complex plane | Every generator that paints it and the shader library's `complexPlane` put `center` in the middle, 3 / zoom units across the shorter side, and the imaginary axis **up**. A generator's `plane` is that framing as a `ComplexPlane`, with `canvasPoint(of:in:)` and `planePoint(at:in:)`. |
| Hue | A turn from 0 to 1 that wraps, in `Color(hue:saturation:brightness:)` and in `OKLCH` and `OKHSL`. |
| 3D coordinates | Right-handed with y up, and the camera looks down its own −z. Nothing draws in 3D until a camera is set with `camera`, `cameraControl`, or the like, and the camera resets every frame. |
| 3D transforms | Three numbers or a `Vector3`: `translate(x, y, z)`, `scale(s, s, s)`, `rotateY(a)` or `rotate(a, axis:)`. The two- and one-number forms, `translate(x, y)`, `scale(s)`, and `rotate(a)`, are the 2D transform and leave a mesh where it was. |
| Field of view | Vertical, in radians, `.pi / 3` by default. A taller canvas keeps the height and shows less width, so a portrait render of a scene framed wide crops its sides. |
| Orbit | `Camera3D.orbiting(target:radius:azimuth:elevation:)` takes radians. Elevation 0 is the horizon and positive looks down from above. Azimuth 0 puts the eye on +z looking toward −z, and π/2 puts it on +x. |
| Spot light | `coneAngle` is the full cone in radians, `.pi / 6` by default. `penumbra` runs from 0 to 1. |
| Color | A `Color` holds sRGB components with straight alpha. Fills, strokes, lights, materials, and tints are converted to linear light for you. `linearRGB` and `Color(linear:green:blue:alpha:)` cross over by hand. |
| Brighter than white | A component can pass 1: `Color(linear: c.linearRGB * 3)`. It only shows brighter than white on screen when the sketch overrides `colorOutput` with `.extended`; otherwise the tone map clips it. |
| Noise | `noise`, `simplexNoise`, and `fbm` return 0 to 1. The `signed` forms (`signedNoise`, `signedFbm`) return −1 to 1. `curlNoise` returns a vector that is not normalized. `noise(loop:radius:)` loops as `loop` runs 0 to 1. |
| Export time | `--frame N` counts from 0 and draws at N / fps, and `--at S` is the same moment in seconds (the frame is S × fps, rounded; it wins when both are given). In `--export-sequence`, file n is drawn at (n − 1) / fps. `--skip` is in seconds and `--start` only renumbers the files. |
| Depth-of-field light | A `SprayLine`'s `light` is the whole segment's, not light per unit length. A long line and a short one at the same `light` put down the same amount of light. |

<a name="params"></a>

### Parameters

`@Param` makes a property tunable. The live hosts show a control for it, and the command line can set it.

```swift
@Param(0...200) var radius = 120.0                     // a slider
@Param("Speed", 0.1...4, step: 0.1) var rate = 1.0    // a label and a step
@Param(1...12) var rings = 5                           // an Int is a stepper
@Param var tint: Color = .purple                       // a color well
@Param(x: 0...1080, y: 0...1080) var origin = Vector2(540, 540)
@Param(group: .folded("Advanced")) var jitter = false  // a card that starts closed
```

- An enum that is `ParamOption` (`CaseIterable`) becomes a menu, and `style: .segmented` shows every case at once. A built-in `Material`, `Easing`, `Fog`, or `Environment` is a menu too (`@Param var surroundings = Environment.studio`).
- `group:` puts a parameter in a titled card, and parameters with no group come first. `icon:` takes an SF Symbol name.
- `$rate.show(when: $mode) { $0 == .fast }` in `setup()` hides a row. The value still holds.
- A value tuned in the inspector lives in that window until "Save parameters to Sketch.swift" writes it into the source. An export never opens the inspector, so it uses the values written in the source. The dot after a tuned parameter's name, or Reset in its row's menu, puts it back to what the line declares, and Reset all puts every parameter back, with the sketch still running.
- `ollin api Param` lists every form a parameter can be declared with.

[Parameters](helpers/parameters.md) has the rest.

<a name="cli"></a>

### The command line

A packaged sketch runs with `swift run <Target> <flags>`, and a single file with `ollin Sketch.swift <flags>`. `ollin <package folder> <flags>` runs a package's sketch the way the single file runs, every file of its target compiled together. With no flag the sketch opens in a window. With an export flag it renders without one and exits.

| Flag | What it does |
| --- | --- |
| `--export frame.png` | One still. `--frame N` picks the frame (from 0, default 0) or `--at S` names the moment in seconds, and `--fps` sets the rate (default 60). Every frame before it is drawn first. |
| `--export-sequence dir` | `frame-00001.png`, `frame-00002.png`, and so on, for `--seconds S` or `--frames N`. `--skip S` runs S seconds before the first file is written. |
| `--export-video out.mp4`, `--export-gif out.gif` | A movie or a GIF, with the same `--seconds`, `--frames`, `--fps`, and `--skip`. |
| `--export-loop out.mp4` | Exactly one lap of `loopDuration`. |
| `--seed N` | The variation to render, set before `setup()`. A `seed(n)` in `setup()` overrides it. |
| `--param name=value` | Sets a parameter, before `canvasSize` is read and before and after `setup()`. Repeat it for more. A color is `#RRGGBB`, a vector `x,y`, a menu its label. |
| `--list-params` | Prints every parameter in the form `--param` takes, and stops. |
| `--settle N` | Draws each written frame N times with the clock held. A `LineSpray` and an `Accumulator` need it to converge. Anti-aliasing over time and path tracing don't. |
| `--render-quality` | `performance`, `default`, or `detail`: the sampling budgets for soft shadows, depth of field, and ambient occlusion. Exports default to `detail`. |
| `--render-scale N` | Draws each frame N times larger and averages it down. It smooths fills and polygons; shapes and strokes are already smooth. |
| `--subframes N` | Draws each frame at N moments across the shutter and averages them: motion blur for anything the sketch draws, 2D included. `--shutter F` sets the opening (0.5 when left out). |
| `--bench` | Times 600 frames and reports the cost per frame, without writing anything. `--gpu` adds the GPU's side. It measures the window's quality tier; an export lifts `.default` to `.detail`, so add `--render-quality detail` to time what an export pays. |

[Export](output/export.md) has every flag.

<a name="lookup"></a>

### Looking something up

Three commands answer most questions without a browser. They read the checkout you build against, so they describe the version you are running.

```sh
ollin api drawCircle           # what a name takes, what its comment says, where it is used
ollin docs Drawing/Color#ramp  # one section of one page
ollin examples ocean --source  # an example that does it, as source
```

- **`ollin api <name>`** answers "what is it called, what does it take, and is it public". It reads the listings under [`API/`](https://github.com/eaviles/Ollin/blob/main/API/README.md), which hold every public declaration, so a name it does not know cannot be reached from a sketch. It prints each declaration as its source writes it, with the comment above it, where units and ranges are written down. `ollin api Mesh.tube` picks one type's member, and `ollin api Param` lists a whole type.
- **`ollin docs <topic>`** opens a page by name or path, and `<topic>#<heading>` opens one section. `ollin docs --search "long exposure"` finds every line that says it.
- **`ollin examples <word>`** lists the sketches whose name, folder, description, or header credit matches (an exact name alone, anything looser together, names first), and `--source` prints one.

`ollin` is `Scripts/ollin` in the checkout, and it works from any folder. `ollin install` puts it on your `PATH`. Its output is plain text under a pipe, so it composes with `grep`. [The reference offline](tools/reference.md) has the rest.

<a name="quiet"></a>

### Things that fail quietly

Each of these gives a wrong or empty picture and no error.

- **A layer that is drawn into but never drawn.** Nothing reaches the canvas until its image does: `drawImage(layer.image, 0, 0)`. A `LineSpray`'s pass count climbs all the same, and the canvas shows only the background until `drawImage(spray.developed(exposure: 1).image, 0, 0)`.
- **Drawing in `setup()`.** The geometry is thrown away before the first frame, and a camera, a light, or shadows set there are gone by the first `draw()`. Draw and set per-frame state in `draw()`.
- **A concave outline through `drawPolygon`.** It fills as a fan from the first point, which only works for a convex shape. A concave one goes through `drawShape { }` or a `Shape`.
- **A `Feedback` or `Accumulator` made inside `draw()`.** A fresh one every frame never builds up. Make it once, in `setup()`.
- **A layer that draws a `filtered(_:)` image.** Every layer draws before any filter resolves, so a target that draws a filtered layer's image this frame comes out empty, as a picture and as data, and a `SimField` cannot take a filtered layer as an input either. A filter's result reaches the canvas, or the next filter in a chain, and nothing else.
- **A data texel with alpha at or below zero.** `drawImage` writes nothing for it, so a signed field copied through `drawImage` into a float layer loses those texels while every other channel arrives verbatim. Keep alpha above zero (one plus the coverage, say) and carry the sign in a color channel, or read the layer where it is with `sampleRaw`.
- **A long, faint sum on a `noClear()` canvas.** The canvas is half-float by default, so a light added a little at a time stops brightening. Use `noClear(precision: .float32)`, `makeFeedback(precision: .float32)`, or an `Accumulator`.
- **A far contour of a distance field measured on a half-float layer.** Past 1,024 pixels a half float holds a distance only to the nearest whole pixel, so a contour cut there comes out ragged. Draw the marks into `makeRenderTarget(precision: .float32)`; the field and every filter after it keep single precision.
- **A still at frame 0.** `--export` draws frame 0 unless told otherwise, and nothing came before it: motion blur is off and a trail or a pile holds one frame. Pick a later frame with `--frame N`. A `LineSpray` or an `Accumulator` is the exception: `--settle N` redraws the frame with the clock held, so it converges even at frame 0.
- **Motion driven by `frameCount` under `--settle`.** The clock holds for the extra draws but `frameCount` keeps counting, so a camera driven by it moves and restarts the converging picture. Drive motion from `time`.
- **Motion driven by `frameCount` under `--subframes`.** Every moment is a draw, so a fixed step once per `draw()` runs N times as fast and does not blur. Drive it from `time`, or step it by `deltaTime`.
- **Values tuned in the inspector, in an export.** The export uses the values in the source. Save them first, or pass `--param`.
- **Values the command line reads loosely.** A `--param` outside the declared range is clamped, an unknown `--render-quality` becomes `detail`, and an `--fps` that does not parse becomes the default. None of them says so.
- **`Filter.grain` in the shadows.** It adds noise and clips it, which lifts black. `.filmGrain` leaves black and white alone, though its grain still shows most in near-black tones.
- **A `@Param` icon that is not an SF Symbol.** The row shows an empty space where the icon goes.
- **3D on a `noClear()` canvas.** Nothing is sorted by depth and nothing casts a shadow.
- **A volume, and the 2D drawn after it.** `drawVolume` with no camera set draws nothing. With one, every volume composites over the frame's whole drawing once it is done, so a caption drawn later in `draw()` lies under the cloud. Draw the caption in `withOverlay { }`. See [Volumes](3d/volumes.md#in-front-of-and-behind).
- **A texture on a mesh with no texture coordinates.** The texture is ignored, and the mesh draws in its base color.
- **`redraw()`, `copyFrame()`, `chooseFiles`, and `pointerShape` in an export.** There is no window, so each does nothing. A `redraw()` inside `draw()` asks for nothing either: that frame is the one being drawn.
- **Noise offsets 256 apart.** Every Perlin field repeats every 256 units on each axis, so copies told apart by an offset of `32 * i` read the same noise every eighth copy. Use an offset that is not a power of two, or a seed per copy.
- **Noise at its ends.** `noise` is clamped after its contrast is raised, so about 2% of it sits exactly at 0 and 2% exactly at 1, and a curve read straight from it runs flat there.
- **A `print` during an exported sequence.** It lands on the same terminal line as the progress text, and later than expected when the output is piped. Write to standard error with a newline first, or read the value after the run.

<a name="checking"></a>

### Checking your work

- **Judge speed in a release build.** `swift run` builds the framework for debugging, which runs several times slower. Use `swift run -c release`, or `ollin`, which runs a release host and compiles the sketch optimized. `--bench` gives numbers.
- **A color or vector parameter reads through a lock.** A number, an integer, or a switch reads in under a nanosecond, like a stored property; a `Color`, a `Vector2`, a palette, or a text value takes a lock, about 40 ns, so read one of those into a local before a loop that would read it per vertex. See [Parameters](helpers/parameters.md).
- **Judge motion in a sequence, not a still.** Anything that needs earlier frames (motion blur, trails, a pile, a settling picture) shows properly only a few frames in. A short `--export-sequence` shows it.
- **Compare pixels, not bytes.** Every exported PNG carries its recipe in its description: the seed, every parameter, the sketch's commit and the framework's, the frame, and the frame rate. Two files with the same pixels differ when any of those do, so compare the pictures, for instance with `compare -metric AE a.png b.png null:` from ImageMagick.
- **Fix the seed before comparing two runs.** Call `seed(n)` in `setup()` or pass `--seed N`, or every run is a new variation.

---

### See also

- [Documentation index](index.md): every page, by area
- [Swift quick reference](swift.md): the language, at the same length
- [The reference offline](tools/reference.md): `ollin docs`, `ollin examples`, and `ollin api`
- [Guide Chapter 1](../guide/01-helloollin.md): the first sketch, run and changed
