Skip to content

A Tour

Field aims to be full of unexpectedly fun features that help you interact with code. This page is going to be a long list of things that delight, things that you didn't think you'd need, and things that help when you are puzzled.

You don't have to read it straight through. Start with running and displaying things, or jump to AI support, drawing on the canvas, or making bigger things. If you want something to run before you look around, Your First Five Minutes begins with a moving drawing. The next stop on the Tour is Frames and the Animation Loop, where a box keeps drawing as time passes.

The Welcome page

The first page of Field is the Welcome page: the first tab in our desktop app, and at https://localhost:9092/welcome in any web browser:

The welcome page in a fresh workspace: a document list with a search box and the Starting points, Templates and Kits disclosures on the left, the Changelog and a stack of collapsible panels on the right

On the left you can make new documents or find that document you just know you edited last week; below the list are Starting points and Templates, curated scenes and scaffolds that open as an editable copy in your workspace. Kits adds reusable packages and their starting points in another disclosure. On the right is a stack of collapsible panels for the rest of Field: the changelog, projects, Maven dependencies, the workspace, the runtime, Vulkan and Python status, AI settings, preferences, and the OSC editor. The search box does full-text search over the whole workspace:

To help you find that snippet of code.

Running things

A Field document is a canvas of boxes. Each box holds a piece of code (and, often, its output); you edit one box at a time in the editor. You can run that code in the following ways (from the commonplace to the rare):

  • alt/option-enter — runs the line or block that the cursor is in.
  • Or, alt/option-enter — runs the currently selected code.
  • alt/option-up arrow — launches the currently edited box. alt/option-down arrow stops.
  • alt/option-click on a box — launches it for as long as you hold the mouse button down, and stops it when you let go. To keep it running after you let go, drag upwards before releasing. To stop it later, alt/option-click it again and drag downwards. For example:
  • alt/option-0 — runs everything in a box (but doesn't launch it)

What's the difference between 'running' and 'launching'? Running executes the code once. A box can end with an object that keeps going by itself, such as a loop { }; launching the box hands that object to Field's animation loop, which calls it once every frame until you stop it. When several boxes are launched, they are called in the same order every frame. The idea of installing code into, and removing it from, the animation cycle is fundamental to making sound, image and data visualizations, so this idea is right at the heart of Field.

That's everything you need to know — with these shortcuts you can have a fun time in Field. Go! Write and run some code against your favourite libraries, in Kotlin (on the JVM), Python, Clojure, even Lua.

But... since running code is very important, we actually have quite a few more ways of doing this. Sometimes just for convenience's sake, sometimes because of subtle differences in what you actually mean by 'run' and 'code'.

For completeness, we'll list them here:

  • alt/option-shift-up arrow — immediately launches the box without recompiling if that's possible. Handy if your box has some long, complicated startup and then an animation or something at the end that you are playing with.
  • ctrl/command-alt/option-k or ctrl/command-alt/option-j increments or decrements the number under the cursor and then acts like you pressed alt/option-enter.
  • alt/option-double-click in the editor. Doing this to true, false, disable { ... } or enable { ... } in the editor flips that word to the opposite and then alt/option-enters the block. Think of this as a toggle, but hanging out in your source code.
  • alt/option-double-click on the name of a function in scope with the signature foo() just runs the function. Buttons everywhere!
  • alt/option-1 — relaunches just the selection, or the line or enclosing block under the cursor (i.e. the same code alt/option-enter would run), without running other parts of the box again. See Running code in a box. You get a handsome stripe in the margin that shows the launched code that's ongoing.
  • Writing code that launches other boxes. For example root.named("progress").begin() launches a box called "progress".

Some of these effects are language-dependent. For example, if you are editing a GLSL shader property, alt/option-enter recompiles and reloads the shader instead.

Printing things

Often your code does something — draws a line, loads a model, makes a sound. But when you are puzzled as to what is actually running, and where, and when you are confused about how your variables are varying, nothing beats a good old print statement. Like its facilities for running code, Field has a few different ways of doing this.

print (and println) always prints next to, or underneath, the line containing the print. If what you print fits on a single line, it sits beside that line; if it doesn't, you get a little block under the line instead:

var x = 10
var y = 20
print(x + y)
var z = 30
println((1..6).joinToString("\n") { "row $it: ${it * it}" })

Field's editor holding the snippet. Beside the print(x+y) line sits 30; under the last println is a black block of six lines, row 1: 1 down to row 6: 36.

You can close blocks with the 'x' symbol in the margin, or by double-tap Esc, which you'll likely be pressing a lot. triple-tap Esc will get rid of all the outputs inside the current editor. You'll be doing that a lot.

print isn't an ordinary function: it's a macro (see below) that knows which line it was written on.

This can be handy in longer, more interesting code, for example:

And it is quite performant, so you can just use these prints as output:

There's a little bit of design here — if they're printing but not changing, they turn white; otherwise, the thought is that the text changing is enough for you to draw your attention to the line.

So, if you don't know what that variable is, drop in a print() and find out.

Printing, but try harder

Appending .h or even .h2 to the thing that you are printing will cause Field to work much harder to make the thing that you are printing a pretty piece of HTML. Here a map holding a list goes to an output area with .h:

val l = [1.0, 2.0, 3.0, 4.0, 8.0, 7.2]
val m = mapOf("a" to "b", "c" to l)
display("m", m.h)

An output panel titled m under a Field box, holding one line:   a → b, c → [1, 2, 3, 4, 8, 7.2] (6)   with the keys in green, the numbers in blue and a small sparkline of the list beside it.

.h2 is particularly suited to lists and maps with lots of data in them — Field intelligently pages in and out just the parts of the data that you are actually looking at, so you can explore gigantic FloatBuffers, for example:

val buffer = java.nio.FloatBuffer.allocate(100_000)
for (i in 0 until buffer.capacity()) buffer.put(i, (Math.sin(i * 0.01) * 10.0 + Math.random()).toFloat())
display("buffer", buffer.h2)

An output panel titled buffer holding a paged view of a FloatBuffer of 100000 floats: a header with its position, limit and capacity and outliner and columns links, then a table with a stride box set to 4, columns c0 to c3 and rows 0 to 7 of rising values.

Visualizers covers .h, .h2 and the rest.

displaying things — print where everyone can see it

We can also 'print' to the canvas using display. For example:

val l = [1.0, 2.0, 3.0, 4.0, 8.0, 7.2]
val m = mapOf("a" to "b", "c" to l)
display("m is equal to ", m.h)

An output panel titled m is equal to, holding the map on one line:   a → b, c → [1, 2, 3, 4, 8, 7.2] (6)   with a sparkline of the list.

puts an output panel, named by the first argument, underneath the box we're writing code in.

Subsequent calls to display overwrite the previous one, giving you a way of 'animating' the output on the canvas.

While the .h and .h2 visualizers try hard to make things pretty, all they are really doing is making some complex HTML in response to what they are trying to visualize. You can make your own as well:

val l = [1.0, 2.0, 3.0, 4.0, 8.0, 7.2]
val m = mapOf("a" to "b", "c" to l)

display("m is equal to ", m.h)

display("just some html", "<div style='font-weight:900'>foo!</div>")

Two output panels under a Field box: m is equal to, holding the map on one line with its sparkline, and just some html, holding the word foo! in heavy bold type.

Browsing things — print where I can interact with it

There are a few more elaborate visualizers for more tangled structures. The "column" visualizer is loosely modelled after Smalltalk's browser:

val l = [1.0, 2.0, 3.0, 4.0, 8.0, 7.2]
val m2 = mapOf("foo" to "bar", "gux" to l)
val m = mapOf("a" to "b", "c" to l, "m" to m2)

column("m", m)

A column browser in an output panel titled m, headed LinkedHashMap 3 entries, listing a String "b", c ArrayList 6 items and m LinkedHashMap 2 entries; a is selected.

This structure updates live if m changes. Similarly, there is tree, which shows the same values as an outline:

val l = [1.0, 2.0, 3.0, 4.0, 8.0, 7.2]
val m2 = mapOf("foo" to "bar", "gux" to l)
val m = mapOf("a" to "b", "c" to l, "m" to m2)

tree("m", m)

A tree browser in an output panel titled m, headed LinkedHashMap 3 entries with a filter box: row a String "b" selected with path and copy buttons, and folded rows c ArrayList 6 items and m LinkedHashMap.

and a viewer for Field's own log, logs(), which shows what the server is logging as it happens:

logs()

A log viewer in an output panel titled internal logging, with a find box and a clear button over timestamped entries tagged info,logstream and debug,logstream: the viewer's own subscribe and ready messages.

Visualizers has more on tree and column.

'printing' (editable) geometry

Field has extensive support for generating 2D and 3D geometry and adding this to the canvas, essentially by displaying bundles of FLine objects. You can see fline_examples for a lot more information. But here's a taste:

:::static
val petal = fline {
    moveTo(0, -120)
    cubicTo(90, -100, 140, -20, 0, 120)
    cubicTo(-140, -20, -90, -100, 0, -120)
    close()
    filled = true
    stroked = true
    thickness = 2.5
    color = vec(0.95, 0.42, 0.6, 0.34)
}

val stem = fline {
    moveTo(0, 120)
    cubicTo(10, 180, -20, 230, 0, 300)
    thickness = 5.0
    color = vec(0.45, 0.95, 0.62, 1.0)
}

lines([petal, stem], w = 420.0, h = 420.0, fitToSize = true, editable = true)

funFeatures-9

It's important to note that you're not just editing a copy of the FLine. This editor mutates the actual FLine object that you passed in — so this is a way of interacting with code, not just witnessing its effects.

3D (and indeed 3D editing) works as well:

See fline_examples for much more.

"Printing" chunks of UI

Sliders, checkboxes, colour pickers and other input controls can be 'printed' into a box's output area too; Visualizers shows how.

Hints as you type

As the cursor moves around the editor, Field shows a hint built from types and current values: what you might type next, the parameters of the function you are in, and the value a variable has right now:

Decorating the editor

Box code can also extend and decorate the text editor itself. In the video below, a musical pattern written in the editor is drawn over as it plays, much as in Strudel, though the language is different:

You can remove output areas you no longer want to see by alt/option-clicking their disclosure triangles; while alt/option is held over one, the triangle turns into a red ✕.

Programmatic control over output areas is through these functions (see Box Output for what each one does, and for the other mouse gestures):

displayOpen("foo")
displayClose("foo")
displayClear("foo")
displayClearAll()

And output areas without content are handy — progress bars are useful for showing you how a box is getting through its processing:

var z = 0
loop {
    z += 1
    progressBar("progress", (z % 10000) / 10000.0)
}

This yields (when launched):

progressBar is the low-level primitive — you drive it and you clear it. For the common cases, there are friendlier helpers that manage the bar for you and clean up after themselves:

// Wrap any iterable / range / count — the bar tracks the loop and removes itself at the end:
for (i in tqdm(0 until 10000)) {
    // ...heavy work...
}

// For routines: report a fraction (with a tqdm-style rate + ETA)...
withProgress("solving") { p ->
    repeat(steps) { i -> /* ...work... */
        p.update(i + 1L, steps.toLong())
    }
}

// ...or .pulse() when there's no known total (an indeterminate "still working" bar):
val p = progressReporter("reducing")
while (notDone()) {
    stepOnce()
    p.pulse()
}
p.finish()

A bar appears only for work that takes longer than about two seconds, and removes itself shortly after the work finishes. Some long-running library routines show one without you asking.

Turn them all off with ProgressBars.enabled = false, or tune ProgressBars.showDelayMillis (the grace period) and ProgressBars.lingerMillis (how long a completed bar sticks around).

AI support

Obviously, it's 2026, so it gets its own page.

Simply put, we no longer expect to sit and write code without AI assistance, and the amount of fun that we can have is partly gated by the number of ways we can interact alongside AI and our code. Field has lots of ways of doing this. We can include agents inside our documents, we can build tools for them, they can inspect, write, compile, execute, screenshot, traverse data structures, fork, rewind, launch, harvest and ask for help. We've launched weeks-long Field processes that build, monitor and reap results from 200 other Field documents.

In Field you can talk code and data structures into existence. Below, a polytope's vertex and fragment shaders each have a small StackSL program in them; six turns of chat made the faces burst outward in a wave and burn out to ragged glowing frames, and in its last reply the model is checking nine real frames of the window before saying yes. That whole conversation, step by step, is Talking to the StackSL in your shader. The same thing works for a function, a shader in a window of its own and an animated RectSL scene.

The polytope box with its AI4 chat restored: the user's last request in orange, asking for ragged glowing frames at the burst and a whole solid at home, then a sheet of nine frames of the polytope bursting and healing, and the model's reply that the live cycle now matches what was asked for

How this documentation is made

This documentation is part of the agents' search context and Field's test system, and is mostly AI-generated. Almost every screenshot under a piece of code is produced by running that code, and reproduced when the codebase changes. Agents extend and revise the prose nightly; see How Field is built.

Multi-language, multi-paradigm

Field's primary language for live coding is Kotlin, which we've found to be expressive and fun (but by no means flawless). We've added a compile-time macro system to it for extra flexibility and joy, and the graphics system does some GLSL-driven code generation. All in all, it's working quite well. Being on the JVM also gives us access to some other options.

Kotlin, with macros

To cure some of the challenges of deploying Kotlin in a live-coding context we've gently extended the language with a macro system. A handful of things that look like function calls in a Kotlin box are really macros, rewritten before the compiler sees them. You have met one already: print knows the line it sits on. prop and the graphics system's moduleHere { } are macros too; Running code in a box goes through them in detail.

This isn't just internal gymnastics: you can write your own macros. A rewrite { } block in a box is a rule over the shape of your source:

rewrite { x: Expr ->
    from { neg(x) }
    to { -x }
}

neg(10) // →  -10

Rules can have guards, bind runs of arguments, run Kotlin at expansion time, and are inherited by child boxes; Rewriting your own syntax is the whole story.

Python

Via the JEP project, Python works splendidly in Field — who among us doesn't need some PyTorch in-process every so often? Python in a box is the full account. Autocompletion is driven by Jedi, and Field's central concept of launching (and stopping) boxes is supported by a custom async runner.

This means, in effect, that you can live code things like OpenCV and Torch with Jupyter-level autocomplete and Field-level AI support. We'd like Field to be good enough to end some people's use of Jupyter. Thanks to JEP, interoperation between NumPy and Field's memory buffers is perfectly reasonable. And code like this:

:::static
from afield.lang.python import PythonInterface

box = PythonInterface.thisBox
box.setName("property access from boxes")

box.setPropertyRaw("foo", 10, False)

This works as you'd expect. We remain ever-so-slightly sketched out by Python's threading model's interaction with ours, in particular its obvious incompatibilities with Kotlin's coroutines — so there may still be bugs here.

Notebook interop (.ipynb)

Because we'd like Field to be an alternative to Jupyter, the notebook loop goes both ways. Drag a .ipynb onto the canvas and choose Import as Python cells: a fresh "python root" box is created, each code cell becomes a Python cell wired under it, and each Markdown/raw cell becomes a notes box indented alongside the code so the prose reads inline. To go the other way, select the Python-root box and run Export notebook (.ipynb) from the command palette (or send the export-ipynb RPC with the box's uid): Field walks the column top-to-bottom and writes a valid nbformat-4 notebook — Python cells back out as code cells, notes boxes back out as markdown cells. Round-tripping (import → edit → export → re-import) preserves cell count, types, and source text.

Clojure

Clojure is another wonderful JVM language that we have a lot of respect for:

:::static
;; Each line of output lands beside the form that made it.
(println "clojure" (clojure-version))

(def modules {:app [:ui :store], :ui [:util], :store [:util]})

(println (count (mapcat val modules)) "edges")

A Clojure box in Field's editor. Beside the first println a chip reads clojure 1.11.3, and beside the last line a chip reads 4 edges. The parentheses are coloured by nesting depth.

Same idea as Python, one runtime further in: Clojure 1.11 is a JVM language, so a Clojure box and a Kotlin box are two objects in one process with nothing between them. (.getChildren (box)) walks the document, an FLine you build in Clojure is the same FLine Field's renderer draws, and returning an animation launches it by the same rule a Kotlin box follows. Printed output lands beside the form that printed it, and so does the exception when a form throws.

And yes, we have rainbow parens. Clojure in a box is the whole story.

Lua

Lua, the scripting language inside norns, Renoise, Reaper and LÖVE, runs in a box through LuaJIT, loaded into Field's own process. A Lua box prints, draws lines, reads and writes box properties, makes sliders, and, launched, gets an update(dt) every frame. Lua in a Box is the full account.

RemoteJS

A JavaScript box runs in a browser tab that Field keeps connected, which can sit right on the canvas beside your code. print and console.log land beside their lines, errors are marked where they happened, launch loops stop when you press Stop, and display, slider and checkbox fill the box's outputs the way they do in Kotlin. The tab is a Three.js scene, so an FLine assigned into stage is drawn there. JavaScript in a browser tab is the whole story, and the same head on a phone or headset is RemoteJS and WebXR.

ForthField & a-Nano

Primarily for teaching purposes, a bizarrely useful live-coding dialect of Forth: stack words, dictionary inspection, recursion, return-stack tricks, helpful diagnostics, Field-native visualization, and full JVM interop:

a-Nano is a little teaching assembly language with completely over-the-top integrated visualizers. We're using (a GPU-accelerated) version of this language for a little ALife research — small enough to run Core Wars-style simulations on GPUs.

Spreadsheets!

The most successful and important programming paradigm of all time — the spreadsheet — has deep integration into Field.

Supported features include integration with other boxes' variables — changing a dependent variable inside a box magically causes an auto-update sweep of the spreadsheet (next animation frame). And a complete, understanding type system: while the expression language for the spreadsheet is Kotlin, there's a very carefully wrapped pseudo-numeric type that auto-promotes gleefully, understanding scalars, scalars with physical units, Field's linear algebra types, and even Field's FLine. If you ever wanted a spreadsheet that could do formulae with drawings, this is it.

Spreadsheets in a Box shows how to make one, what formulas can do, and the editing keys.

Actors / Message-passing

We can think about 'the box' as taking on the role of "an actor". This marries very well with animation in general (the choreography of things over time) and Kotlin's flexible coroutine system. Given two boxes:

:::static
onMessage<Vec2> {
    reply(payload.lengthSquared())
}
:::static
onMessage<Vec3> {
    100.alphas {
        /// animate from payload up to something sle
        frame()
    }
    reply(payload.lengthSquared() + 10) // reply
}

We can start sending messages around. Messages propagate upward until they find a box that has an onMessage<T> of the correct type.

Where am I? How did I get here?

Field keeps document backups as you work. The history view lets you inspect those saved versions and restore an earlier state.

You can open history in two ways:

  • run Show Document History from the command palette
  • or open /history?file=<document-name> directly on the Field server

The document history view with version #1 of a three-version document selected, and the editor showing that version as an inline diff against the current code

Along the top is a timeline of every saved version, which you can scrub and view by save or by real time. You can compare each version against the current document or against the one before it, narrow everything to the selected box, search for the versions that touched a piece of text, blame a line, and name a version so you can find it again. Restoring can take the whole document back or cherry-pick just the boxes you select, and every restore is itself recorded, so it can be undone. Document History walks through all of it, and Code History scrubs a single line back through its versions without leaving the editor.

Documentation browser

In some weird inception move, Field has a documentation browser (Browse Documentation or ctrl/command-/):

Press that cyan arrow to get back to the text editor.

Beside the page is a DOC NOTES panel. Select a sentence, press Ctrl+Shift+A and say what's wrong with it; your note joins a queue that agents, one per page, work through: they revise the Markdown behind the note and leave a record of what they did, or a question for you. Notes on the Documentation explains the loop.

Presenting with Field

Your talk can be a Field document too. Put Markdown, images, real webpages, YouTube and live Field documents on a separate audience display, with crossfades between slides and simple fade-in builds. When it's time to demonstrate something, run a saved macro or an interactive guide in the document on screen, and edit the code while the audience watches. Slides, CSS themes and shared media stay in ordinary files you can keep in Git.

Long talks get a collapsible outline and incremental search over slide titles and their text, including reveals. Type a phrase, choose a match with the arrow keys, and press Enter to jump there; searching leaves the audience on the current slide until you're ready.

Field's presenter searching for propagation in a slide's reveal text, with a matching excerpt and a keyboard-selected result.

Start with the Presentations kit on Welcome. Presenting with Field walks through making a talk, styling it and teaching from the live material.

A few keyboard shortcuts for getting around Field.

ctrl/command-shift-P — pressed in the editor, this brings up the command palette for that box; press it again and you get the command palette for the document as a whole:

Double-tap command/control (command on Mac, control on Windows/Linux) in the editor gives you an affordance that lets you use the cursor keys to switch boxes:

Double-tap shift opens the 'open anything' search box:

Drag the little grip in the top-left corner of the canvas to split it into panes, like a spreadsheet's split. Panes in a row scroll up and down together and panes in a column scroll side to side together, so a row of headings or a column of labels stays lined up with the boxes it belongs to while you move around the rest. Drag a divider to move it; drag it to the edge to get rid of it. Moving around the canvas has the details.

Field's canvas split into four panes by a black cross: bar headings along the top and instrument labels down the left stay lined up with the pattern boxes in the scrolled body

ctrl/command-f does the Find you'd expect inside the editor; but with the keyboard focused on the whole document, finds over the contents of all of the boxes:

Layers

Field boxes exist in layers, and boxes' visibilities and positions are per layer. Inside a box, you assign it to layers with layers:

layers = "default,secondLayer"

A box that names no layer is on default. alt/option-L (Show Layers Panel in the command palette) opens the layers panel, which lists every layer some box is on:

The layers panel: four rows, default, overlay, render and sketch, each with an eye button, a radio button, the layer name, its box count and a Solo button, all in aligned columns. overlay is hidden: its eye is dimmed and so is its row. default is the active layer: its radio is checked and its name is in the accent colour. Below the rows are Show all and Move selection to active layer buttons.

Each row has:

  • an eye, which toggles whether the layer's boxes are on the canvas. Shift-click it to show that layer alone. The last visible layer cannot be hidden.
  • a radio button for the active layer. The active layer decides which set of frames you are moving when you drag boxes about. Making a layer active also shows it.
  • the number of boxes on the layer
  • Solo, which shows only this layer and makes it active

Once the drawing has anything on it, it gets a row too, called drawing, with an eye to show or hide it and a count of its marks. It has no radio button or Solo, since boxes can't go on it. Show all brings it back with the rest.

Show all brings every layer back, and Move selection to active layer puts the selected boxes on the active layer, and only on it. alt-] solos the next layer and alt-[ the previous one; the command palette labels include the target layer names. Show All Layers in the command palette shows every layer and opens the panel.

The panel floats over the canvas, so hiding a layer never hides it. Layers has the whole story, including a different layout on each layer. showLayers() in a box puts the same controls in that box's output area instead:

layers-panel

Drawing on the canvas

alt/option-d turns the canvas into a drawing surface: pens, shapes, arrows and text, in Excalidraw's hand-drawn style, right over your boxes. alt/option-d again and you're back with the boxes, and the drawing stays put, panning and zooming along with everything else. Pinch to zoom, by the way; that works everywhere now.

Field with the drawing layer switched on: Excalidraw's toolbar across the top of the window, over a score drawn by a box, with its blue piano-roll rectangles, a red glissando and its label, a green held note and an orange playhead, and the box that drew them to the left

The fun part is that boxes can read the drawing and draw on it. notation { arrow(from = thisBox, to = other) } draws an arrow that follows both boxes around; notation.onStroke { } hears every stroke you make; and notation.space(...) lets you read a region like a score, cutting it at an instant and asking what's there. Here's Cage's Fontana Mix, read by a box:

Six coloured curves in a frame labelled 'fontana mix', a grid, a point and a straight line, with the box's readout of where the line crosses the curves

There's a whole page on it: Drawing and Notation.

Keyframes and timelines

One of the reasons that Field even has a 2D infinite canvas for code is so that the space of the boxes can become meaningful. Traditionally, in 'time-goes-from-left-to-right' apps (Premieree, After Effects, DaVinci Resolve, Motion, etc.), there's usually a way to edit a function over time with the mouse. Field has a really very fully featured keyframe editor available for boxes to use:

keyframes(channel = "k", name = "keyframe", defaults = [vec(0.2, 0.5), vec(0.5, 2.0)])

gets you started. Run it, and that puts a keyframe editor in the box's output area holding the two default keys.

keyframes(channel = "k", name = "keyframe", defaults = [vec(0.2, 0.5), vec(0.5, 2.0)])

A keyframe editor in an output panel titled k/keyframe: a grid with values 0 to 2 up the side and a blue line between two keys, one at time 0.2 and value 0.5, one at time 0.5 and value 2, and a small orange playhead marker at time 0.

Scroll with scroll gestures, zoom with ctrl-scroll, double-click to add a keyframe, right-click to change the keyframe type.

These boxes are both input and output, so this code, which converts a keyframe set into a Field Fn (see Functions), is:

val k = keyframes(channel = "k", name = "bob", [0.0, 1.0])
val k2 = keyframes(channel = "k", name = "bob2", [0.0, 1.0])

k.onChange = {
    with(Functions) {
        val f = fromKeyframeEditor(it)
        val f2 = gaussianSmooth(f, sigma = 0.1)
        f2
    }
}

This gets you a smoothed curve that's still editable, by editing the underlying keyframes that make it.

Once you have a keyframe, you can do a few things with it:

val k = keyframes(channel = "k", name = "bob", [0.0, 1.0])

k.setTime(0.5) // move the orange playhead
val v: Double = k.evaluateValueAt(0.5)
val dv: Double = k.evaluateDerivativeAt(0.5)

Edited keyframes are stored inside the box — the defaults argument is just there in case the box is new. The keyframe editor has the rest: every gesture, the five key types, overlays, and a starting point to open.

Time sliders & Custom UI

Inserting a field.timeSlider template from the f1 menu gets you a box that a) draws itself as a vertical line and b) launches and stops boxes it passes over:

For the extra curious, this slider is just a regular box with some customizations to its on-canvas appearance and behavior:

// export this box so that any box can now write 'execution' to talk about this box
rootProp<Box>(execution) = thisBox

fun launchFrom(was: Double, now: Double) {

    val all = root.children - thisBox // candidates for execution
    val a = all.filter { it.frame.intersects(was) }
    val b = all.filter { it.frame.intersects(was, now) }
    val c = all.filter { it.frame.intersects(now) }

    val incoming = c - a
    val outgoing = a - c
    val onlyOnce = b - (a + c) // starting and stopping

    incoming.forEach(Box::begin)
    outgoing.forEach(Box::end)

    onlyOnce.forEach {
        inMain {
            it.begin()
            yield()
            it.end()
        }
    }
}

// whenever the viewport changes run this to keep the line covering the view
onViewportChange +=
    "lock_x" to
        { inside, a, b ->
            frame = frame.copy(y = -b.y - 100, h = b.h * 2 - 50, w = 40.0)
        }

// whenever the frame of the box changes run this
onFrameChange +=
    "lock_x" to
        { inside, a, b ->
            launchFrom(a.x, b.x)
            b.copy(y = -inside.viewport.y - 100, w = 40.0)
        }

// just set the width to be 40.0
frame = frame.copy(w = 40.0)

// set a custom border, and lock the height
customization =
    """
    this.style.border = "0px solid transparent"
    this.style.borderLeft = "3px solid red"
    this.style.background = "transparent"
    this.style.marginTop = "0px"
    this.style.paddingTop = "0px"
    this.style.setProperty("--box-border", "0")
    this.style.fontStyle = "italic"
    this.style.color = "white"
    this.lock_w = true
    this.lock_h = true
"""

name = "t"

autorun = true

Custom Marking Menus

There are lots of other ways of customizing Field's front end from within Field. One, higher level, is to introduce a custom marking menu:

customMarking["N" to "bob"] = { print("North!") }
customMarking["SE" to "something"] = { print("se!") }
customMarking["W2" to "the end"] = {
    print("W!")
} // outer ring

You can open and swipe around these callbacks with alt/option-shift-click:

This is nothing more (but also nothing less) than Field speaking to the long tradition of code-writing tools being able to extend and write themselves.

Snapping

When caps-lock is lit, boxes snap to other boxes when you move them around or resize them — good for aligning things when we're in timeline mode.

You'll also find a couple of different 'align' and 'organize' commands in the document-level command palette for tidying up after yourself.

Making bigger things

By default, every box of code is pretty isolated from all of the other boxes. This is often what you want — otherwise, you are constantly wondering if something you write in one box will break another piece of code somewhere else. But sometimes you want boxes to communicate. Field does this in a couple of ways.

Shared properties

We can connect boxes together using the connect command, or by selecting the parent and then the child and pressing g:

(To disconnect, select and then press 'u'.) Now that these two boxes are connected, they can opt into sharing certain things.

In the Parent:

prop<Double>(foo) = 10.0

This means that in the children:

print(foo)

This prints 10.0. You can think about this mechanism as 'exporting' particular variables (with types) to child boxes. prop here is a macro that expands and runs some set of glue code that makes a real Kotlin property backed by the right kind of storage. This glue code is stored in the preamble property. This code accumulates but it's essentially a layer of inherited auto-regeneratable glue code between boxes and their children.

Anything that has an exportable type in Kotlin can travel — this includes Kotlin lambdas, Vec3 etc. But alas, not types defined inside boxes.

Properties also support lazy initialization:

prop<Vec2>(myVec)

once.myVec {
    // `myVec` will stay the same for this whole session
    vec(1, 2).noise(10.0)
}

once.myVec { ... } computes when the property is null and caches a non-null result on the box. (null itself is not cached.) This is useful in the graphics system because a box often wants to create one window and Vulkan renderer, then publish those resources to descendants through prop. When the value depends on something — a view made for a particular renderer — use cached<View>(renderer) { View() } instead, which makes a new one (and closes the old) when the key changes; see cached.

This can end up as a kind of reactive variable system for Kotlin. For the mechanism behind all of this — how prop/once expand, how properties propagate up the box graph, the @exported annotation, the typed accessors moduleHere { } generates, and the //%- fragment break — see Running code in a box.

Nesting

Boxes can be nested inside other boxes: select them, open the command palette (ctrl/command-shift-P) and pick "Nest ...":

This allows you to encapsulate boxes inside other boxes to work together toward a common aim.

Nested parents can be referenced inside nesting children with nestedIn (Box?) and nesting parents can get a Set<Box> of their nest children with nested. If there are non-visible nesting children that are acting as children or parents to boxes that are visible, that is indicated in the connections: the connection is drawn to the nesting parent instead, ending in a short bar with a label.

Referencing

Finally, named("foo") returns a Box? called "foo", searching from the current box 'downwards' through the hierarchy if it exists. Similarly root.named("foo") searches starting at the root of the document — a virtual parent to all boxes.

Collaborating

Field's back-end just serves pages to any browser that contacts it; it's just usually the case that Field is running on the machine that you are using to look at its interface. But if you can conspire to open a webpage on that server, you can do so from anywhere on earth (OpenEndedGroup particularly likes Tailscale for this, but there are other options).

Multiple browser clients receive the same server-side box and property updates. Field attempts to preserve non-overlapping editor changes, but simultaneous edits to the same text are not a promised conflict-free collaboration protocol.

Multiple editors

The document-level command "Add Editor to Output Area" pins a particular property of a box to that box's output area:

These additional, fully functional editors give you a place to keep code (or outputs embedded in code!) that you keep coming back to.

Extra screen time

The other browser worth pointing at Field is the one in your pocket. Field serves a page at /controllers that is built for a phone rather than shrunk down to fit one: a large touch pad that sends X, Y or both into the selected box, the sliders and colour wells that box publishes, its output areas reformatted into a single narrow column, the real AI4 conversation for it, Open Anything, and a full Monaco editor on whatever is selected. There is one selection state behind all of it, shared with the desktop — the phone follows Field's, and a pick made on the phone becomes Field's — so a piece can be running on a projector while you hold its controls and walk around in front of it. On the left is a box in the desktop app, its code open in the editor; on the right is the same box on the phone, the Group tab at iPhone size showing the step sequencer it published with controlGroup(...): you sweep the bars and paint the grid with a finger, and the playhead the box drives moves across both. See Sequencer controls for the code:

The Field desktop app with the same box: on the canvas the sequencer box and its open output panel with the velocity bars, the pattern grid, the band slider and the wave menu; beside it the code editor open on the box's Kotlin, the controlGroup declaring the four controls and the loop that drives the playheads The Group tab of the Field mobile controller at iPhone size showing a step sequencer: a velocity multislider of sixteen bars, a four-row pattern grid with cells in red, green and blue and an orange playhead on the sixth column, a band range slider and a wave menu reading saw, with the six tabs along the bottom

Field from your phone has the URL, what each of the six tabs does, and the warning that goes with any of this: the page can run boxes, edit code and call AI4 tools, so it belongs on a network you trust and never on the open internet.