---
title: Triggers
navTitle: Triggers
slug: manual/triggers
kind: guide
section: Manual
order: 215
status: active
summary: The scheduling layer — controlling when parts of a program run
---

# Triggers

Triggers control **when** parts of a Utomata program run.

While fields and formations describe values, triggers act on the scheduler: they can stop or resume fields, reset their state, advance them manually, or perform batch-output actions at specific moments.

Triggers are declared with the `@` sigil:

```uto
@freeze(step == 16) {
  stop(#A);
}
```

This trigger fires when the global step reaches `16` and stops `#A` from advancing.

> A direct comparison refers to an exact step value. If the clock has already passed `10`, this trigger will not fire later. Live edits do not restart the clock; a global reset or `set()` call onto a tested field is required to return it to `0`. This is particularly important during live coding: adding the trigger above after the program has already reached step `500` will not cause it to fire.

A trigger contains one predicate and one or more actions:

```uto
@name(predicate) {
  action(...);
  action(...);
}
```

Triggers are not Utomata expressions. They use their own small syntax for describing scheduler conditions and commands.

## Predicates

A predicate determines when the trigger fires.

The simplest form compares the global clock, as in the example above. `step` is the global scheduler step and begins at `0`.

```uto
@freeze(step == 16) {
  stop(#A);
}
```

A trigger may instead follow the local step of a particular field:

```uto
@freeze(#A.step == 16) {
  stop(#A);
}
```

This distinction matters when fields run at different rates. The global `step` advances on every scheduler tick, while `#A.step` advances only when `#A` itself advances.

### Periodic Predicates

Modulo predicates create repeating events:

```uto
@init(step == 0) {
  stop(#A);
}

@sample(step % 10 == 0) {
  step(#A);
}
```

This fires at steps `0`, `10`, `20`, and so on, effectively emulating `every = 10;` on #A. This mechanism can be useful to create multi-pass rendering sequences where Field #A might need more than one tick for every tick of #B. 

The same form can use a field-local clock:

```uto
@sample(#A.step % 4 == 1) {
  ...
}
```

The interval must be a positive integer, and the phase must fall within that interval:

```uto
step % 10 == 0   // valid
step % 10 == 9   // valid
step % 10 == 10  // invalid
```

Clock predicates currently use `==` only.

### Field-Value Predicates

Triggers can also respond to a sampled field value:

```uto
@converged(#A[0, 0].x > 0.98) {
  set(#A);
}
```

A cell predicate contains:

- an explicit field lookup
- a component: `x`, `y`, or `z`
- either `<` or `>`

The lookup coordinates are literal normalized field coordinates, not Utomata expressions.

> Field-value predicates are evaluated through an asynchronous GPU readback. They therefore use the most recently available sampled value rather than the exact current-frame value. For this reason they are best suited to coarse conditions such as convergence, thresholds, or state changes and should not be relied on for tick-exact timing.

They also fire on the **crossing** of the condition. If the value rises above the threshold, the trigger fires once. It must fall below the threshold and cross it again before the trigger can fire a second time.

## Firing Behavior

The predicate forms differ in how often they fire. A direct clock comparison is a one-shot event: it fires when the condition first becomes true. A periodic predicate fires on every matching step, and a field-value predicate fires on each threshold crossing.

| Predicate | Behavior | synchronous
| --- | --- | --- |
| `step == N` | fires once when the clock reaches `N` | yes |
| `#A.step == N` | fires once when the field reaches local step `N` | yes |
| `step % N == P` | fires on every matching global step | yes |
| `#A.step % N == P` | fires on every matching field step | yes |
| `#A[x,y].x > v` | fires on each false → true threshold crossing | NO! |

Triggers do not currently support boolean composition such as `&&`, `||`, or `!`. Each trigger contains one predicate.

## Controlling Fields

Four trigger actions control fields directly and work during normal realtime execution:

| Action | Description |
| --- | --- |
| `set(#A)` | re-evaluate the field's `set` expression and reset its local step |
| `run(#A)` | resume a stopped field |
| `stop(#A)` | stop the field while preserving its current state |
| `step(#A)` | advance the field exactly once on the next scheduler tick |

Several fields may be targeted by the same action:

```uto
@stopAll(step == 100) {
  stop(#A, #B, #C);
}
```

### Set

`set()` restores a field to the state defined by its `set` property:

```uto
@restart(step == 100) {
  set(#A);
}
```

It also resets that field's local `step` to `0`.

### Stop and Run

`stop()` pauses recurrent evaluation without removing or clearing the field:

```uto
@freeze(step == 20) {
  stop(#A);
}
```

The field retains its last committed state and can continue to be sampled or rendered.

`run()` resumes normal scheduling:

```uto
@resume(step == 40) {
  run(#A);
}
```

`run()` does not itself evaluate the field; it simply allows it to advance normally again.

### Step

`step()` schedules one manual field advancement:

```uto
@advance(step == 20) {
  step(#A);
}
```

The advance occurs on the next **global** scheduler tick, ignoring the field's local `every` property. 

## Batch Actions

Triggers also provide actions for controlling offline and batch execution:

| Action | Description |
| --- | --- |
| `log(...)` | read field data into an output channel or diagnostic log |
| `flush()` | flush pending output |
| `end()` | end the batch run |
| `next()` | advance an input binding or combination |
| `halt()` | pause batch execution until externally resumed |

These actions run **only in batch or offline execution**. They are ignored during normal realtime execution: a `log()` trigger produces output as part of a batch run, but not while the same program is running interactively. Input bindings and the `next()` action are described further in the **Input & Output** chapter.


### Logging

A whole field can be logged:

```uto
@snapshot(step % 10 == 0) {
  log(#A, frames);
}
```

or a single cell can be sampled:

```uto
@probe(step % 10 == 0) {
  log(#A[0, 0], values);
}
```

The second argument names the output channel.

A keyless form is also available for diagnostic output:

```uto
@probe(step % 10 == 0) {
  log(#A[0, 0]);
}
```

### Ending a Run

A batch program can terminate itself explicitly:

```uto
@finish(step == 300) {
  end();
}
```

This is useful when the duration of a computation is defined by the source rather than by the host command.


## Reference

General syntax:

```uto
@name(predicate) {
  action(...);
}
```

### Predicates

| Form | Description |
| --- | --- |
| `step == N` | global step comparison |
| `tick == N` | alias of `step == N` |
| `#A.step == N` | field-local step comparison |
| `step % N == P` | periodic global trigger |
| `#A.step % N == P` | periodic field-local trigger |
| `#A[x,y].x > v` | sampled field-value threshold |
| `#A[x,y].x < v` | sampled field-value threshold |

Clock predicates support `==`. Field-value predicates support `<` and `>`.

### Realtime Actions

| Action | Description |
| --- | --- |
| `set(#A, ...)` | re-seed fields and reset their local step |
| `run(#A, ...)` | resume fields |
| `stop(#A, ...)` | pause fields |
| `step(#A, ...)` | schedule one manual advancement |

### Batch / Offline Actions

| Action | Description |
| --- | --- |
| `log(...)` | read field or cell data |
| `flush()` | flush output |
| `end()` | finish the run |
| `next()` | advance an input binding |
| `halt()` | halt until externally resumed |

Triggers form the scheduling layer of a Utomata program. They do not compute values themselves; they observe clocks or sampled state and use those conditions to control how computation proceeds.





