Getting it into your agent
One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.
npx agentmods add skills/plausibleventures/lattice/startingnpx skills add plausibleventures/lattice --skill startinggit clone --depth 1 https://github.com/plausibleventures/latticeWrote this? Show the measurements
A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.
[](https://agentmods.dev/skills/plausibleventures/lattice/starting)<a href="https://agentmods.dev/skills/plausibleventures/lattice/starting"><img src="https://agentmods.dev/badge/skills/plausibleventures/lattice/starting.svg" alt="Measured on agentmods" height="20"></a>What it costs to keep this loaded
Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.
| Model | Per session | Once invoked |
|---|---|---|
| Fable 5 | $0.00123 | $0.04875 |
| Opus 5 | $0.00062 | $0.02438 |
| Sonnet 5 | $0.00025 | $0.00975 |
| Haiku 4.5 | $0.00012 | $0.00487 |
Grade A, and why
starting scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 4d ago.
A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.
Nothing flagged
None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.
How it starts
The opening of the file, as written. The whole thing — 392 lines — stays where its author put it; the contents beside it link to each section on GitHub.
Starting a Lattice game
Nine packages, one boot file, and an order that is not obvious. Get it wrong in two specific places and there is no error, no warning, and a picture that looks plausible — the whole reason this skill exists.
The layering, so you never have to guess which package a thing is in:
core ─┬─▶ iso ──┬─▶ draw ─┬─▶ ui
├─▶ loop │ │
├─▶ sim └─────────┤
├─▶ persist │
├─▶ input ──────────┘
└─▶ audio
core imports nothing. Nothing imports ui. If you find yourself wanting an upward import you
have the design backwards, not a missing export.
The boot, in full
This compiles and runs. Copy it, rename it, and change the world it builds — not the order.
import { createScope } from '@latticekit/core';
import { DepthSorter, createCamera, tileBounds } from '@latticekit/iso';
import type { Rect } from '@latticekit/iso';
import {
BASE_SLOTS,
beginFrame,
createCanvas2dSurface,
createLightField,
createPalette,
endFrame,
isoTile,
renderFrame,
} from '@latticekit/draw';
import type { Passes } from '@latticekit/draw';
import { browserFrames, createLoop, createTweens } from '@latticekit/loop';
import { createInput } from '@latticekit/input';
// ── the screen ────────────────────────────────────────────────────────────────────
const host = document.getElementById('app') ?? document.body;
const canvas = document.createElement('canvas');
canvas.style.cssText = 'display:block;width:100%;height:100%';
host.append(canvas);
const scope = createScope();
const surface = createCanvas2dSurface(canvas);
const palette = createPalette(BASE_SLOTS);
// ── the world, and the camera that has to be told about it ────────────────────────
const W = 160; // not 64. See "the shape a first build takes"
const H = 160;
const MAX_HEIGHT_PX = 96; // the tallest ground on the map
const worldRect: Rect = { minX: 0, minY: 0, maxX: 0, maxY: 0 };
tileBounds(0, 0, W, H, MAX_HEIGHT_PX, worldRect);
// The OPENING SHOT is a region of the world, never the whole of it. A fresh camera looks at
// world (0, 0) — in a 2:1 projection the *top corner* of the map — so with no fit at all the
// first frame is empty space beside the world. Fitting `worldRect` is the opposite failure:
// every corner of the map on screen at once, which is the diorama the next section is about.
const opening: Rect = { minX: 0, minY: 0, maxX: 0, maxY: 0 };
tileBounds(W * 0.3, H * 0.3, W * 0.4, H * 0.4, MAX_HEIGHT_PX, opening);
const camera = createCamera(Math.max(1, innerWidth), Math.max(1, innerHeight), {
bounds: worldRect, // where the player may go: the whole map
minZoom: 0.25,
keepVisible: 0.5,
});
camera.fitBounds(opening, 24); // where they start: a part of it
// ── the night. Built unconditionally: it costs nothing while darkness is 0 ─────────
const light = createLightField(surface, { scale: 0.6, falloff: 1, bloom: 0.3 });
const order = new DepthSorter(512); // allocated once, reused for ever
const tweens = createTweens();
// ── the clock, BEFORE the input, because the input needs it ────────────────────────
const loop = createLoop({
clock: { now: () => performance.now() },
frames: browserFrames(), // rAF paints; an interval ticks when hidden
});
const input = createInput({
element: canvas,
camera,
step: loop, // the loop itself. Never a number
terrain: 'flat', // this ground IS level, and says so. The moment
// it grows a heightfield: { field, maxHeightPx },
// or every tap resolves at sea level. See `input`
actions: { touch: ['tap'] },
});
// ── one resize handler, so there cannot be two that disagree ───────────────────────
function fit(): void {
const w = Math.max(1, innerWidth);
const h = Math.max(1, innerHeight);
// `surface.pixelRatio`, never `devicePixelRatio` — the surface already clamped the device's
// ratio, and re-reading the raw one here silently undoes that.
surface.resize(w, h, surface.pixelRatio);
camera.resize(w, h);
camera.fitBounds(opening, 24);
}
addEventListener('resize', fit);
visualViewport?.addEventListener('resize', fit); // iOS: a collapsing URL bar fires only this
scope.add(() => {
removeEventListener('resize', fit);
visualViewport?.removeEventListener('resize', fit);
});
fit();
// ── the two wirings it is fatal to cross ──────────────────────────────────────────
let daylight = 1;
loop.onUpdate((dt, tick) => {
input.tick(tick); // BEFORE the game's update: a handler must see the world as the
// player left it, not one step behind it
daylight = 0.5 + 0.5 * Math.cos(loop.realTime * 0.1);
tweens.step(dt); // AFTER: a tween started this step should not also advance in it
});
const passes: Passes = {
maxHeightPx: MAX_HEIGHT_PX, // or a summit vanishes when its own base leaves the bottom edge
terrain(pen, visible) {
for (let gy = visible.gy0; gy < visible.gy1; gy++) {
for (let gx = visible.gx0; gx < visible.gx1; gx++) isoTile(pen, gx, gy, 'ground');
}
},
solids(pen, sorted) {
for (let i = 0; i < sorted.count; i++) {
const index = sorted.indexAt(i);
void pen;
void index; // draw the thing at `index` here
}
},
};
loop.onRender((_alpha, time, nowMs) => {
input.frame(nowMs); // the camera's glide integrates here, at display rate
const pen = beginFrame({ surface, camera, palette, t: time, clear: 'sky', light });
light.begin(pen, 1 - daylight, 'night'); // darkness 0–1, and the color the dark goes
order.clear();
// …fill `order` with everything on screen…
renderFrame(pen, passes, order); // renderFrame calls sort() itself
endFrame(pen);
});
// ── teardown, and the one line that saves an hour under Vite ──────────────────────
function dispose(): void {
loop.stop();
input.dispose();
light.dispose();
scope.dispose();
canvas.remove();
}
if (import.meta.hot) import.meta.hot.dispose(dispose);
loop.start(); // nothing runs before this. No ambient loop, no autostart
What this file has done since we first saw it
Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.
- 4d ago First seen · 392 lines · 123 tokens per session scan A e333861f7945
starting is a skill published in the GitHub repository plausibleventures/lattice (35 stars, last pushed 11d ago), licensed MIT. It adds 123 tokens to every session and 4,875 once invoked, about $0.0006 per session on Opus 5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.
Other skills, from other repositories
phaser3-engineer
!cat skills/shared/game-visual-foundations.md 2>/dev/null || echo "=== Visual Foundations not loaded ===" !cat skills/shared/protocols/ux-protocol.md 2>/dev/null || true !cat skills/shared/protocols/input-validation.md 2>/dev/null || true !cat skills/shared/protocols/tool-efficiency.md 2>/dev/null || true !cat…
dedicated-server
Use when building dedicated servers — headless export, server architecture, lobby management, and deployment.
dialogue-manager
Use when using the Dialogue Manager addon — .dialogue files with titles, responses, conditions and mutations, runtime balloons, and C# support.
event-bus
Use when implementing decoupled communication between nodes — global EventBus autoload with typed signals.
godot-optimization
Use when optimizing Godot games — profiler, draw calls, physics tuning, memory management, and common bottlenecks.
limboai
Use when using the LimboAI addon — behavior trees and hierarchical state machines (C++ GDExtension) with a visual editor, BTTask subclassing, and a blackboard.