Opens slither.io, enters a nickname, and plays automatically using two concurrent threads. The main thread runs a 50ms movement loop that steers the snake through alternating wide and tight orbit patterns. A background worker thread continuously analyses screenshots using pixel-level connected-components detection to spot enemy snakes — no vision API calls, sub-200ms latency. On threat, the snake snaps into a tight defensive spin that is very difficult for human players to intercept. Hold Shift at any time to take manual control; release and the bot resumes after 3 seconds.
Main thread
App.defaultBrowser().open()— opens slither.ioAccessibilityTree+setValue()— fills the nickname field via the AX treeGroundingModel.default()— used twice at startup: to click the Play button and to locate the snake's eyes for precise canvas calibrationinitLogger(null, 'warn')— silences verbose simulang mouse/screenshot logsMouseController.moveMouse()— steers the snake by orbiting the cursor around the canvas centreKeyboardController+Key.VolumeMute— spams the mute key as a visible panic indicatorWorker(Node worker threads) — runs the pixel detection loop in parallel without blocking movement
Worker thread
screenshotCropped()— captures a 1200×800 region centred on the snakeScreenshot.base64()— extracts image data without touching diskJimp.read()— decodes the image in memory for pixel access- Connected-components BFS with 8px radius connectivity — counts distinct bright regions after brightness thresholding; ≥ 2 large components = enemy present
AskModel.default().ask()— checks every 30 iterations whether the game-over screen is visible (the only API call in the hot loop)
Prerequisites:
- Simulang installed (
simulang runavailable in your terminal) OPENROUTER_API_KEYrequired — see setup instructionsnpm installrun once in this folder- macOS (uses
osascriptfor Shift key detection)
Steps:
cd slitheriosimulang run main.ts
Hold Shift to take manual control. The bot resumes 3 seconds after you release.
[Open slither.io]
→ [AX tree: set nickname "_"]
→ [Vision grounding: click Play]
→ [Vision grounding: find snake eyes → calibrate (cx, cy)]
→ [Triple-tap mute as startup sanity check]
Main thread (50ms ticks):
[Check Shift → manual override?]
→ [Orbit cursor: wide loop ↔ tight loop, alternating direction]
→ [On panic: switch to micro panic-spin radius]
→ [Mute spam every 5 ticks while panicking]
Worker thread (parallel, ~150–200ms per cycle):
[screenshotCropped 1200×800 → base64 → Jimp decode]
→ [Brightness threshold → binary map]
→ [BFS connected components, 8px radius]
→ [≥ 2 large components → postMessage({ threat: true })]
→ [Every 30 iterations: AskModel game-over check]
→ [Save debug_binary.png to simulang-experiments/ every 20 iterations]
Main receives postMessage → PANIC ON/OFF → terminate on game over
- Calibration — the snake's head is always at the centre of the game canvas. Grounding on the snake's eyes at startup gives the exact pixel coordinates regardless of browser chrome height or window position.
- Connected components — bright pixels (average RGB > 80) are thresholded into a binary map. The 8px connectivity radius bridges small gaps in the snake body without merging well-separated food pellets.
MIN_COMPONENT_SIZE = 800filters out pellets and glow effects. - Debug image — every 20 worker iterations a black-and-white
debug_binary.pngis saved to yoursimulang-experiments/folder showing exactly what the algorithm sees. Open it to tuneBRIGHTNESS_THRESHandMIN_COMPONENT_SIZE. - Concurrency — worker threads share the parent process's screen capture permissions (unlike spawned child processes on macOS), which is why the detection loop lives in a
Workerrather than achild_process.spawn. - Manual override —
isShiftHeld()usesosascriptwith JXA to readNSEvent.modifierFlagsdirectly, checking every 5 ticks (~250ms) so it doesn't block the movement loop.
