Skip to main content
T1 fan-out · creators / media: nika:glob lists the folder, for_each fans one nika:image_fx task per photo, and a fixed ops chain (grayscale → dither → pixelate) renders each one. No model in the loop — the pipeline is pure pixels, so the same input produces the same bytes on every run, on every machine.

The job

Batch-stylizing a shoot means opening an editor, replaying the same five clicks per photo, and hoping you replayed them identically. This workflow IS the preset: the ops chain is written once in YAML, the batch fans out in parallel, and re-running it on the same pinned engine with the same input and ops yields the same bytes. An engine upgrade is a new pin — re-render before treating an old hash as current.

The shape

The file

image-fx-batch.nika

Two things this file teaches

  1. The ops vocabulary is a closed set — dither: { mode: bayer4 } is one of the engine’s named modes; misspell it (bayer) and nika check answers with the whole legal set instead of guessing. The checker teaches the vocabulary before the run spends a cycle.
  2. A fan-out that can produce nothing still binds — the collect step guards with ${{ tasks.stylize.output != null ? … : [] }} (quoted, because a bare CEL ternary contains : and YAML would claim it). An empty folder degrades to an empty manifest, not a crash.

Run it

Rehearsal writes nothing:
To run a file you own: copy the YAML on this page to fx.nika, point const.photos at an existing directory of PNG files, then:
nika compile does not instantiate this showcase slug and does not plant sample photos. nika compile --list names exact skeletons; only a Ready candidate plus an explicit destination writes.