Ollin

Ollin

A Metal-rendered creative-coding framework for Swift on Apple platforms.

Run a sketchStart with the Guide

Guide

Creative coding from zero, taught through Ollin in 45 chapters.

Reference

218 pages, one for each type and helper, grouped the way you meet them.

Examples

633 runnable sketches by category, each with its source.

Ollin is for generative art, live visuals, and installations. A sketch runs on the Mac and on iPhone, written and worked on from the Mac. The techniques are built in: Voronoi and L-systems, raymarched signed-distance fields, GPU fluids, on-device computer vision, and the rest of the catalog. The renderer sits directly on Metal and composites in linear light. The tooling covers the work up to a finished piece: live reload while you edit, a typed parameter inspector for tuning, deterministic headless export for the final render, as a still, a video, a vector file, or a web page.

The API borrows the friendly setup()/draw() feel of p5.js, the typed core of OPENRNDR, and the simple structure of openFrameworks, reimplemented in Swift idioms rather than ported (see Influences & attribution). Ollin (OH-leen) is the Aztec glyph for movement, the seventeenth day sign of the calendar.

Coming from p5.js or Processing? Appendix C of the Guide maps the API you already know onto Ollin. From openFrameworks or OPENRNDR, Appendix E covers the big moves.

  • Platform: macOS 26+ and Swift 6.3+ (Xcode 26), on any Mac that runs them. Apple platforms only, by design
  • Rendering: Metal, built on Foundation / SwiftUI / MetalKit / simd; no package dependencies; the C/C++ libraries it does use (Box2D, Jolt, libtess2, Clipper2, Syphon, and a few smaller pieces) are vendored under External/ with licenses intact and provenance recorded (details)
  • License: MIT
  • Built with: an AI coding assistant (Claude) under @eaviles's direction; see Built with AI

Status: alpha, pre-1.0, built in public. The rendering, the color, and the output are production-grade. The API is not frozen yet: names and signatures still change, and there's no stability or support guarantee. Choose a release or pin a minor version, and read the changelog before upgrading. See Status & contributing for what to expect.

Examples

Every one of these is the whole program.

Hello, circle

import Ollin

@main
final class HelloCircle: Sketch {
    override func setup() {
        // optional one-time setup
    }

    override func draw() {
        background(.white)
        noFill()
        stroke(.black)
        strokeWeight(3)
        drawCircle(width / 2, height / 2, 120 + sin(time) * 40)
    }
}

That's the whole program: a black circle outline, breathing on a white canvas. @main boots the window for you, and the draw loop is already running, so there's no call to start the animation, and time (seconds since start) is ready to use. Delete + sin(time) * 40 and you have a still circle. Or change width / 2, height / 2 to mouseX, mouseY and the circle follows the pointer: mouseX, mouseY, mouseIsPressed, and a keyPressed() override are already on the sketch. The shipped Basic/HelloCircle is this same sketch, drawn a little larger, with its sizes multiplied by the built-in scale factor so they hold at any canvas size.

Spokes

import Ollin

@main
final class Spokes: Sketch {
    let palette = CosinePalette.neon
    let count = 64

    override func draw() {
        background(.black)
        let c = center
        let maxLen = shortSide * 0.42
        let inner = maxLen * 0.18
        for (i, t) in fractions(count).enumerated() {
            let a = t * .tau + time * 0.1
            let pulse = wave(1.5, amplitude: 0.5, around: 0.5, phase: Double(i) * 0.3)
            let outer = inner + maxLen * map(pulse, 0, 1, 0.25, 1)

            strokeWeight(map(pulse, 0, 1, 6, 26))
            stroke(palette.color(at: t + time * 0.05))
            drawLine(polar(a, inner, around: c), polar(a, outer, around: c))
        }
    }
}

Sixty-four spokes, each breathing on its own phase. Run it

VisualSynth

import Ollin

@main
final class VisualSynth_Example: Sketch {
    override func draw() {
        drawVisual(
            .oscillator(frequency: 24, speed: 1.2, colorShift: 0.35)
                .kaleidoscope(segments: 6)
                .displaced(by: .noise(scale: 2.5, speed: 0.25), amount: 0.12)
                .rotated(time * 0.05)
                .blended(with: .voronoi(scale: 8, speed: 0.4)
                                    .contrast(1.5)
                                    .tinted(Color(hex: 0x3346FF)),
                         .add, amount: 0.3)
                .saturation(1.3)
        )
    }
}

A chain of visual operators, with no shader file to write. Run it

Physarum

import Ollin

@main
final class Physarum_Example: Sketch {
    var slime: Physarum!

    override func setup() {
        slime = makePhysarum(agents: 220_000, resolution: 1024)
    }

    override func draw() {
        stepPhysarum(slime)
        drawImage(slime.image, in: bounds)

        drawCaption("Physarum · 220,000 agents growing a transport network")
    }
}

Two hundred and twenty thousand agents, on the GPU. Run it

Run it

Before you start: macOS 26 or later, and Xcode 26 installed for its Swift 6.3+ toolchain. You never have to open Xcode itself. Any Mac that runs macOS 26 has the graphics support Ollin needs. Check the compiler with swift --version. The first build compiles the framework from source and may take several minutes; later builds are incremental.

Then, in a terminal:

git clone https://github.com/eaviles/Ollin.git
cd Ollin
swift run --package-path Examples Example-Basic-HelloCircle

You should see a black circle outline breathing on a white canvas. Try one edit: close the window, open Examples/Basic/HelloCircle/Sketch.swift, change stroke(.black) to stroke(.red), and run the last command again. The circle now breathes in red.

More than five hundred runnable sketches live in Examples/, grouped by topic. From the repo root, swift run OllinExamples opens a gallery with all of them in a sidebar. The examples' running guide covers individual targets and browsing from the terminal.

The canvas is 1080×1080 by default, previewed fit to your screen. canvasSize sets the resolution a sketch renders and exports at, and windowMode sizes the preview window. The Canvas page covers the presets and how to write resolution-independent sketches. Coordinates use a top-left origin with y increasing downward, the same as p5, Processing, and OPENRNDR; Where a point is covers units and other frames.

Install

For a standalone sketch

From the clone created above, install the command:

Scripts/ollin install

Follow any PATH instruction it prints. Then, in the folder where you keep sketches:

ollin new dots.swift        # write a starter sketch
ollin dots.swift            # open a live window; edit the file and save to reload

That install also puts zsh completions in place. Press Tab after ollin for the subcommands, or after a -- for the flags of whichever command you are typing. If something does not work, ollin doctor says what this machine can run and what to do about each answer. Checking the machine covers it.

Keep the clone: ollin links to it and uses its framework version. Its first run builds a release host, so allow a few minutes even if you already ran Hello Circle. To stay on a release rather than the tip, check out the newest tag in that clone, and read the changelog before moving to a later one. Single-file sketches covers assets, extra imports, directly executable files, and growing into a package.

In an existing Swift package

Add these entries to your Package.swift, then put a sketch like Hello, circle in Sources/MySketch/Sketch.swift and run swift run MySketch:

// Package.swift
platforms: [
    .macOS("26.0")
],
dependencies: [
    // Pre-1.0: a breaking change bumps the minor, so stay on one minor.
    .package(url: "https://github.com/eaviles/Ollin.git", .upToNextMinor(from: "0.15.0"))
],
targets: [
    .executableTarget(
        name: "MySketch",
        dependencies: [.product(name: "Ollin", package: "Ollin")]
    )
]

The platforms entry matters: without it SwiftPM targets its oldest macOS default, and the build fails against Ollin's macOS 26 floor. .upToNextMinor(from: "0.15.0") accepts 0.15.x fixes and keeps out 0.16.0, where APIs may change. Read the changelog before raising that bound.

What's in it

All of it is one API with the same conventions throughout.

  • The output is production-grade. Every frame composites in linear light with HDR tone mapping, dithered output, analytic anti-aliasing, and up to 8× MSAA. Color is OKLab with real gamut mapping, not HSB approximations. Exports are deterministic and carry a reproducibility recipe: the seed, the parameter values, the commit. Rendering is snapshot-tested against committed reference images.
  • Edit, save, see it change. Live reload recompiles just the sketch and swaps it into the running window. Typed @Param controls keep their values across reloads, and dragging a shape changes the numbers in your own source. The inspector also shows what the frame cost: CPU against GPU, draws, and passes.
  • Live coding on stage. OllinLiveCoding puts the code over the visuals for the audience. Evaluate on command, mid-motion, with the clock and tuned parameters carrying across the swap. A typo leaves the last good sketch playing.
  • A Metal core you can extend. Most shapes render as analytic signed-distance fields (one instanced quad each, so thousands of moving shapes stay cheap), strokes carry their own anti-aliasing fringe, and frames composite through an HDR float pipeline. When you outgrow the built-ins, write your own fragment shader or compute kernel without leaving the framework.
  • Motion is the default. draw() runs at the display's refresh rate from the first line, so 120 + sin(time) * 40 is already an animation. Under that is a motion layer: frame-rate-independent easing and springs, keyframe timelines, parameters on their own curves, input smoothing, noise that closes exactly over a lap, perfect-loop export, and motion locked to MIDI clock.
  • Made to be left running. Installation mode fills the screen, hides the pointer, and keeps the display awake. The clock survives display sleep and long runs; @Saved state survives a relaunch. A watch restarts a crashed piece, a schedule gives it the building's hours, and projection calibration and edge blending fit one canvas across displays or projectors.

Everything above ships in this repository. The catalog is the full capability map, with a reference page for each area. Beyond drawing and shaders, browse seventy-three generative techniques, simulation and physics, the opt-in 3D layer, on-device perception, data, sound, and control surfaces and rig integration.

More about Ollin

↑↓ move ↩ open