OhMyGame
OhMyGame is the open-source studio for making games with AI. Design, build, playtest, and publish games in one app, with your own model keys.
Eat, grow and outsmart AI worms. Plays offline.
Translation pending
GamesJavaScript · CSSGitHub @tamslerOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Wigglefeast: an offline snake-style .io game with AI worms, saves and install-as-app support.
Taken from the repository README
Loading the full README.
Read the full README ↗A single-player snake-style .io game you can play offline. Eat, grow, and outsmart AI worms; there's no server.
Online: https://tamsler.github.io/wigglefeast/
Install it as an app (works offline afterwards):
Locally: open index.html in a browser (double-click works, nothing to build). Offline/install support needs it served over http(s), e.g. python3 -m http.server.
Run into another worm's body and you die. Make others run into yours and eat what they leave behind. Head-on collisions are won by the bigger worm. The red ring is the arena edge. Bigger worms are drawn over smaller ones, so a worm wrapping another is on top of it, and a smaller worm's body under your own body can't hurt you: you're on top of it there. Your head always stays on top, so you can see what's right in front of you.
Growing massive: any worm that keeps winning can grow huge, into the hundreds of thousands and, rarely, the millions. Worms only start shedding mass above 2,000,000 (0.2% of the excess per second). Above 1,000 mass, arena food is worth more the bigger you are (its value times (mass / 1,000)^0.6: about 4x at 10,000 and 16x at 100,000), though not food that worms drop or special food, and bots get the same. Past 10,000 worms grow mostly thicker rather than longer (and past 100,000 hardly longer at all, up to a radius of 300); the camera zooms out as you thicken. Boosting costs 1.5 + 0.25% of your mass a second, at most 150. A dead worm's food is still 80% of its mass, in pieces that get bigger with their value. Every new world starts with at least 3 giants (20,000 up to 35,000 / 55,000 / 80,000 mass on Easy / Normal / Hard; one per 10 bots in bigger worlds) to look up to and hunt.
Power-ups: 🧲 magnet (blue and red waves ripple forward from the worm's head as far as it pulls food) · x2 / x5 / x10 food multiplier (arena food only, not what worms drop; x10 is the rarest) · ⚡ free boost + speed · 🔭 zoom out · 🌀 tight turns (16s each).
Special food: nova crystals (+25), nebula diamonds (+50) and plasma spheres (+100) turn up at random, about 5 at a time in a 30-bot arena (more in bigger ones). Most are crystals; plasma spheres are the rarest (about 1 in 10). Each lasts 20 seconds, blinks for its last 4 and then vanishes if nobody has eaten it. They look the same in every food theme, the magnet pulls them in like food, and x2 / x5 don't apply to them. Bots go for them too.
The arena grows with the bot count: up to 60 bots its radius is 1800 + 60 per bot, and above that its area grows in step with the bots, so a big world is bigger, not more crowded.
Difficulty: Easy bots steer clumsily and rarely hunt. On harder levels bots see further, react faster and use more tactics: they predict where a turning worm is going, avoid heads about to cross their path, get out of the way of a cut-off, go head-on at smaller worms, and go for remains nobody else is closer to. They put a meal before a fight: they drop a chase, even a coil, when remains nearby are worth more than the kill. On Hard every bot also traps worms between other bodies, pins them against the wall and circles them, and works its way out when it's circled: it follows the inside of the loop instead of being squeezed in the middle, and slips out through any gap it can turn into, without boosting into the loop. Hard bots also shadow worms too big to hunt (up to four times their mass, you included): they run alongside, two at most, and cut in at the head when a clear cut opens, and two bots hunting the same worm cut in from opposite sides. They are sharp, too: they re-plan more often, look farther and check their path closely, weigh more ways out, notice cutters from farther away and steer clear of several at once. They also don't boost into a turn they can't make: a boosting worm turns wider. A third of them start large, up to mass 5,000. (Hard is what Expert was before there were three levels; saved Expert settings, worlds and scores carry over as Hard.)
Hard bots also learn how you escape a cut-off, across games: whether you usually turn away from the bot, turn toward it or keep straight, and whether you boost. A bot cutting you off then aims where you usually go and leads farther if you usually boost. They only watch what anyone could see (your worm's turns and boost), the effect grows slowly with evidence and is capped, and recent escapes count most, so changing your style beats them again within a few cut-offs. A small 🧠 by a bot's head shows it's watching how you escape. Reset stats makes them forget.
Bots that read other worms (some on Normal, all on Hard) keep their distance from a worm they just saw kill, and hold a grudge against one that tried to cut them off: they come after it if they can (never more than two at once), or keep away if it's too big. That includes you: cut a bot off and it may come after you for a while.
Bots only use what you could see on screen, never your steering input, and treat you like any other worm.
Difficulty and growth numbers live at the top of engine.js (CFG, DIFFICULTY, including each level's tactic shares). node tools/balance.js runs headless worlds and reports survival, kills (by the killer's tactic), bot sizes and time per tick for each level; --set tries other values without editing the engine (for example --set hard.tactics.coil=0). Each report prints its seed; --seed replays the same worlds, so two settings can be compared on identical runs.
Everything is saved automatically in the browser's localStorage, so there's nothing to do by hand:
.json file and loads it back. Use it as a backup or to move to another browser/computer. Clearing your browser's site data erases the in-browser save.Storage keys: wormarena.world, wormarena.profile, wormarena.settings, wormarena.habits (the prefix predates the rename and is kept so existing saves load).
engine.js: simulation (worms, food, collisions, bot AI). No DOM, so it can run headless in Node.main.js: canvas rendering, camera, input, HUD and menus.index.html, style.css: page and UI.manifest.webmanifest, sw.js, icons/: installable app + offline support and release versioning.tools/: balance.js (headless balance report) and make-icons.js (draws the app icons). Not shipped with the game.tests/: engine and browser tests (see Tests).openspec/: specs and change plans (see Planning).main is protected: every change lands through a pull request, and the Tests and Release version bumped checks (the Tests and Version check workflows) must pass before it can merge. Merging to main deploys to GitHub Pages, but installed copies only pick up a release whose version changed:
VERSION at the top of sw.js (e.g. '1.1.0' → '1.2.0') in the same PR as the change. The Version check on the PR fails with "Bump VERSION in sw.js" if you change a game file without doing this. Changes to README.md, LICENSE, openspec/, .github/, .claude/, tests/ and tools/ don't need a bump.v<VERSION> and publishes a GitHub Release with notes generated from the merged PRs (versions with a suffix like 1.2.0-beta.1 are marked pre-release). Players get the new release downloaded in the background and see an Update available → Reload banner (never mid-run). Ignoring it is fine; it takes over on the next launch.main (SemVer order, so 1.6.0-beta.1 < 1.6.0). To roll back, publish the old code under a higher version.When a release adds a new file the game loads, add it to FILES in sw.js too, or it won't work offline.
You can run the checks locally: .github/scripts/check-version.sh main and DRY_RUN=1 .github/scripts/release-tag.sh.
The icons in icons/ (192 and 512 px, Apple touch icon, favicon) are drawn in the brand colours by a script: run node tools/make-icons.js (it needs Google Chrome, like the tests) and commit the PNGs it writes, with a VERSION bump. Keep the file names: the manifest and service worker refer to them. Chrome and Android pick up a new icon with the update; iOS keeps the old home-screen icon until the app is removed and added again.
node --test "tests/**/*.test.js"
No dependencies: Node 22+ and Google Chrome (set CHROME_PATH if it isn't in the usual place).
CI splits the files into five parallel shards balanced by duration: node tests/shard.js 2/5 runs shard 2 locally. Every file runs in exactly one shard, and a new file needs no setup. Add a slow one to SECONDS in tests/shard.js to keep the shards even.
tests/simulation.test.js runs whole worlds on every difficulty and at 150 bots. tests/engine.test.js covers save round trips and save validation, growth and shedding, food and power-ups. tests/bots.test.js tests each bot tactic in a hand-built scene, tests/habits.test.js learning the player's habits, and tests/coil.test.js coil escape. Shared helpers are in tests/helpers/engine.js.tests/browser/ drives headless Chrome through the DevTools protocol:
tests/release.test.js checks the release rules: versions only go up and are never reused.tests/fixtures/save-v1.json is a real save in the current format; it must keep loading.The Tests workflow runs the suite on every PR that touches code. A PR that only changes docs and specs (top-level Markdown, openspec/, .claude/, LICENSE, .gitignore) skips the shards, and the required Tests check still passes (.github/scripts/needs-tests.sh). Pushes to main always run everything.
Work is tracked in GitHub Issues. Feature-sized changes are planned with OpenSpec under openspec/ (/opsx:propose, /opsx:apply, /opsx:archive in Claude Code); openspec/specs/ describes how the game currently behaves.
MIT © 2026 Thomas Amsler