Tags
Group nodes. Query in formulas.
Tags are string labels you attach to nodes. Tag a row of bricks #brick. In a formula, Tag.brick.length returns the count. Destroy a brick — the count updates reactively.
Adding tags
Select a node. In the properties panel header, click the tag area. Type comma-separated tags:
brick, destructible, row1
Tags appear as badges in the properties panel and element tree. Multiple nodes can share the same tag. One node can have multiple tags.
Using tags in formulas
Access the group
Tag.brick // → array of all nodes tagged "brick"Tag.brick.length // → count of brick nodesTag.brick[0] // → first brick nodeTag.brick[0].x // → x position of first brick
Reactive updates
Tag.brick.length is reactive. When a brick is destroyed, every formula that reads Tag.brick.length re-evaluates automatically.
title: "Brick counter"
description: "Score tracks how many bricks remain — click bricks to destroy them"
nodes:
- type: RECT, name: "Brick1", x: 100, y: 100, w: 60, h: 30, fill: "#3498DB", tags: ["brick"]
- type: RECT, name: "Brick2", x: 180, y: 100, w: 60, h: 30, fill: "#2ECC71", tags: ["brick"]
- type: RECT, name: "Brick3", x: 260, y: 100, w: 60, h: 30, fill: "#E74C3C", tags: ["brick"]
- type: RECT, name: "ScoreBox", x: 180, y: 250, w: 100, h: 40, fill: "#2C3E50", cornerRadius: 6
formulas:
- node: "ScoreBox", prop: "width", value: "Tag.brick.length * 40 + 20"
events:
- node: "Brick1", event: "click", code: "this.destroy()"
- node: "Brick2", event: "click", code: "this.destroy()"
- node: "Brick3", event: "click", code: "this.destroy()"
tools_visible: [select, play]
ui_visible: [properties, formulas, layers]Tag registry
Tags are stored in a global registry: Map<tag, Set<nodeId>>. Lookups are O(1).
When a node is destroyed:
- It's removed from all tag sets
markDep('Tag', tagName)fires for each tag- All formulas reading that tag re-evaluate
When tags are added/removed via setTags():
- Registry updates
- Change callback fires
- Dependent formulas re-evaluate
Type checking with tags
The is() method checks both type and tag:
node.is("RECT") // true if node type is RECT
node.is("brick") // true if node has tag "brick"
node.is("enemy") // true if node has tag "enemy"In collision events:
if (event.target.is("brick")) {
event.target.destroy()
}Visual features
Canvas highlight
Hover a tag badge in the properties panel — all nodes with that tag show a diagonal stripe fill and blue outline on the canvas.
Element tree highlight
Hovering a tag also highlights matching nodes in the layer tree with a CSS class.
Select by tag
Shift+click a tag badge — selects all nodes with that tag. Useful for bulk operations.
Autocomplete
Type Tag. in a formula — autocomplete shows all registered tag names from getAllTags(). Type Tag.brick. — autocomplete shows VNode properties (x, y, width, height, rotation, etc.).
Serialization
Tags serialize as an array on each node:
{ "type": "RECT", "tags": ["brick", "row1"], ... }Backward-compatible migration handles old tag string format → tags array.
Common patterns
Win condition
WinText.visible = Tag.brick.length === 0
Enemy count display
Counter.text = Tag.enemy.length
Spawn tracking
// In a method: create node, tag it, formula sees itnewBullet.setTags(["bullet"])// Tag.bullet.length increases automatically
Group operations
// In an event handler: destroy all enemies
for (const enemy of Tag.enemy) {
enemy.destroy()
}