On this page
First model: a spreading fire
Manual contents | Neighborhood reference | Tools and utilities | Project settings | Next: Wandering turtles
This tutorial assumes you can edit a small NetLogo model: you understand
variables, if, and a rule repeated each tick. You do not need previous
experience with JavaScript classes or the NL API.
We will build a deliberately simple model, not a realistic fire prediction:
- Every patch starts as a tree, except one burning patch in the center.
- A tree catches fire if any of its eight neighbors is burning.
- A burning patch becomes burnt on the next tick.
- A burnt patch stays burnt.
There are no moving turtles yet. The patches themselves hold the model state.
1. Find the three pieces
| Piece | What you put there | Rough NetLogo comparison |
|---|---|---|
| Project | World size, palette, inputs, displays, execution options | Interface and world settings |
| Main | Create populations; count and publish results | Observer procedures |
| Patch | The rule each patch follows | Code inside ask patches [...] |
class FirePatch extends NLPatch means "make our patch type using the supplied
patch behavior." this means the object currently executing the code.
this.main is that patch's route to the model-wide Main object.
super calls the supplied base behavior rather than replacing it.
In Studio:
- Download a backup of your current app if you want to keep it. New App replaces the current workspace.
- Choose New App, enter Fire, select Cellular Automaton, and choose OK. The editor opens; Turtle is not needed for this pattern.
- Replace the Project, Patch, and Main tab contents with the corresponding definitions below. Do not paste all three into one tab.
- Choose Save All, then Launch. Allow the app window to open if asked. The editor's Test command checks syntax only; use the observations below to check the model itself.
The code uses the module layout produced for an NL patch/agent app.
Main class Fire and Patch class FirePatch match the app name.
Keep the generated app shell/import wiring. Main and Patch below have default
exports as Studio-generated classes do. Project is configuration, not a class.
Existing standalone apps sometimes use named exports instead; preserve their
entry-point imports if adapting one.
The shipped templates are still legacy. Use the complete definitions here
rather than keeping template constructor calls to init() or assuming the
template already has the aligned flag. If the editor retains a Turtle source,
it is unused: remove the Project agent section and Main's initAgents().
2. Configure the world
Use this Project definition:
const Project = {
version: 0.2,
name: "Fire",
title: "A spreading fire",
lifecycle: "aligned",
frameWidth: 1170,
frameHeight: 1000,
connect: true,
visitMode: "C",
dt: 1,
cell: {
cellSize: 12,
rowDim: 21,
colDim: 21,
boundary: "Edge",
palette: ["#228b22", "#ff4500", "#303030"],
start: [10, 10, 1]
},
displays: [
{
code: "BURNING",
site: "display_01",
type: "linechart",
dom: [0, 25],
ran: [0, 100],
width: 460,
height: 275,
legend: ["Burning patches"],
palette: ["#ff4500"]
}
]
};
lifecycle: "aligned"selects the initialization/reset rules used throughout this guide. It belongs at the top level, besidename, not insidecell."C"tells the runtime to execute patch rules, not turtle rules.21rows and columns give us 441 patches. Coordinates start at zero."Edge"means the fire does not jump from one edge to the opposite edge.- A patch's numeric
colorselects a palette entry:0is green,1orange,2dark gray. Here color doubles as the model's three-state variable. startcontains column/row/color triples.[10, 10, 1]makes the center patch burning; all other patches start at color zero.- The display code
"BURNING"is the name Main will use to send its count.display_01must be a display slot in the generated app layout.
3. Give each patch its rule
Use this Patch source:
import {NLPatch} from "@nwk/api/nlpatch.js";
import {Enum} from "@nwk/api/tools.js";
const FireState = Enum(
{name: "Tree"},
{name: "Burning"},
{name: "Burnt"}
);
class FirePatch extends NLPatch {
setup() {
this.main.visitAlways(this);
}
patchStep() {
super.patchStep();
let nextState = this.color;
if (this.color === FireState.Tree.ord) {
let burningNearby = false;
this.neighborVisit(neighbor => {
if (neighbor.color === FireState.Burning.ord) {
burningNearby = true;
}
});
if (burningNearby) nextState = FireState.Burning.ord;
} else if (this.color === FireState.Burning.ord) {
nextState = FireState.Burnt.ord;
}
this.nextColor = nextState;
}
}
export default FirePatch;
Read it a little at a time
Enum gives our states names and numbers: Tree has .ord 0, Burning 1, and
Burnt 2, matching the palette. It is not a NetLogo breed or a population.
See Enum for more examples.
setup() runs when the world is ready and again after Reset.
visitAlways(this) puts this patch in the set that gets visited each tick.
Selecting "C" alone does not activate every patch. Repeating this registration
is safe because the runtime uses a Set: the same patch is not added twice.
For this example, do not also enable visitAll.
neighbor => { ... } is a small function (a callback). neighborVisit
calls it once for each neighboring position. In this square Edge world it sees
up to eight neighboring patches, without the center patch.
This is similar to examining NetLogo neighbors.
this.color reads the patch's state now. this.nextColor = ... proposes
its state for the end of the tick. All patches therefore read the same old
world, and the fire travels only one neighborhood outward per tick. The
runtime commits the proposals automatically; do not call shift() yourself.
Do not replace the last line with this.color = nextState: that would change
state immediately and replace the Reset baseline. Likewise, currentColor
would change state immediately, making spread depend on visit order.
super.patchStep() preserves the base step's behavior, including any public
color updater assigned later. We have not assigned an updater here.
4. Create the patches and count the fire
Use this Main source:
import {NLSimMain} from "@nwk/api/nlsimmain.js";
class Fire extends NLSimMain {
constructor(Patch) {
super();
this.Patch = Patch;
}
initCells() {
this.initializePatches(this.Patch);
}
setup() {
this.publishBurning();
}
update() {
this.publishBurning();
}
publishBurning() {
const patches = this.patches.toList();
const burning = patches.filter(patch => patch.color === 1).length;
this.show("BURNING", burning);
}
}
export default Fire;
The runtime supplies the Patch class to Main's constructor. We save it as
this.Patch and create the world in initCells(), not in the constructor.
initializePatches creates the patches and applies Project's initial colors.
toList() gives us an ordinary JavaScript array of patches.
filter(patch => patch.color === 1) keeps only burning patches; .length
counts them. This is like count patches with [pcolor = ...].
show("BURNING", burning) publishes a number to the configured chart.
Main's setup() publishes the initial count after patch setup is finished.
update() publishes again after each tick has committed its state.
We do not need to override init() or reset() for this model.
5. Step, run, and reset
Launch the saved app and wait for connection (or use Connect if automatic connection is disabled). Before clicking Run, check these milestones:
| Time | What to see | Burning | Burnt |
|---|---|---|---|
| 0 | One orange patch in a green world | 1 | 0 |
| 1 | Dark center, orange ring around it | 8 | 1 |
| 2 | A larger orange square ring | 16 | 9 |
| 10 | The orange ring reaches the world's edges | 80 | 361 |
| 11 | The whole world is dark | 0 | 441 |
Use Step first. Then try Run and pause. Reset should restore time zero and the one burning center patch without reloading the page. Step twice, reset again, and check that the first two counts are still 8 and 16. This simple model has no randomness, so these checks should be exact.
If nothing changes, check visitMode: "C", Main's initCells(), and the patch
setup() registration. If the whole forest burns in one step, check that the
rule writes nextColor rather than color or currentColor.
6. Understand the lifecycle without learning the plumbing
| Place | Job in an aligned model | This tutorial |
|---|---|---|
| Constructor | Allocate/save stable objects; no manual init/setup calls | Main saves Patch class |
init() |
Initialize once after defaults/context exist | No custom work needed |
setup() |
Work requiring the completed world; repeat after reset | Activate patches; publish count |
reset() |
Restore the existing object's local state | Inherited patch reset restores color |
The browser Reset command coordinates the whole world. If writing code
that requests the same operation, use main.resetWorld(), not main.reset().
Main and patches survive reset; turtles, when present, are recreated.
NetLogo's usual setup procedure often does several of these jobs together.
Here the runtime owns creation and reset order. You supply hooks for your work,
not calls to replay that order yourself.
7. Small experiments
Make one change at a time and predict the result before running:
- Two fires. Set
start: [7, 10, 1, 13, 10, 1]. Where do the fronts meet? Reset restores both seeds; the table above no longer applies. - Four neighbors. Replace the
neighborVisit(...)call withthis.main.nhood4(this).forEach(...), retaining the same callback body. The front becomes diamond-shaped. With"Edge"it stops at the boundaries; with"Wrap"it crosses to the opposite side. See the neighborhood reference. - Uncertain spread. Import
NLRootfrom@nwk/api/nlroot.jsin Patch and change the ignition line toif (burningNearby && NLRoot.random() < 0.4) nextState = FireState.Burning.ord;. This is one ignition chance per exposed tree per tick, not per burning neighbor. Some trees survive; repeated runs may differ.
Continue with Wandering turtles for movement, live sliders, and agent counts. Read Managing model state when a variable needs more than a color, and the aligned lifecycle contract when adding turtles, births, or relationships between objects.