We Built a Game IDE in the Browser and Lived to Brag About It
Hey, we're Evolved Tech — the crew behind Limn Studio, a browser-based game IDE where you can build, run, and publish 2D games without installing a single thing. No npm. No webpack. No 45-minute node_modules folder that somehow weighs more than the actual game. Just us, a browser, a questionable amount of caffeine, and an unreasonable number of <span> tags.
You'll mostly hear from Kehinde and Desire in this one, but there's a wider crew behind the scenes at Evolved Tech who deserve just as much credit for keeping this thing running — you know who you are, thank you.
Somewhere along the way we also lost four hours to a bug that turned out to be a smart quote sneaking in from a pasted Google Doc. We're still not over it.
Grab a coffee. This is the full, slightly unhinged tour of everything we built, why we built it, and the one CPU-saving trick that finally stopped our animations from moving like they were wading through molasses.
Part 1: The Editor Shell (or: How to Build an IDE Out of Spite and <div>s)
Here's a fun fact that will make actual frontend engineers wince: Limn Studio's editor is one HTML file. No framework. No build step. No bundler crying softly in the corner about peer dependencies. Just <style> and <script> tags, stacked like a JavaScript lasagna.
Why? Because every dependency you add is a tiny gremlin that wakes up in six months, demands a major version bump, and takes your whole app down over a null check nobody asked for. No gremlins allowed here. This thing deploys to any static host and will probably still run in 2040, long after everyone else's React app has dependency-rotted into digital compost.
The layout has four zones: a top bar, tabs, main content, and a floating action button that pops open like it's got somewhere important to be. Very dramatic. Very "we contain multitudes."
Mobile: The Great Padding Massacre
Turns out, on a 380px phone screen, our lovely 24px of padding on each side of the editor was quietly eating 12% of the horizontal space. Twelve percent! That's not padding, that's a landlord. So on mobile we ripped out every border, radius, and ounce of breathing room and went full edge-to-edge. The editor now goes commando on small screens, and everyone's happier for it, especially the pixels.
Part 2: The Code Editor (Handmade, Like Artisanal Bread, But It's a Syntax Highlighter)
This is where we lost the most sleep. We did not build a mini Monaco. We do not have the staffing, the sanity budget, or frankly the will to live required for that. What exists instead is about 400 lines of vanilla JavaScript doing an extremely convincing impression of a real code editor, and getting away with it.
The trick: stack two elements directly on top of each other like a syntax-highlighting sandwich.
- A
<pre>on the bottom layer, rendering pretty colored<span>tokens - A
<textarea>on top with the text made completely invisible (color: transparent), but the caret stays visible
The textarea handles all the boring-but-critical native stuff — selection, undo, autocorrect, whatever mysterious ritual iOS performs when you triple-tap — while the <pre> just sits there looking pretty. It's a magician's assistant setup: one element does all the labor, the other gets all the applause.
The Tokenizer: 80 Lines, Character by Character, No Regrets
We hand-wrote a tokenizer that walks the code one character at a time and slaps a <span class="tok-*"> on everything — keywords, strings, numbers, comments, operators, punctuation, function calls, the works. No parser library. No AST. Just vibes and a while loop. And somehow it works beautifully, in GitHub Dark Modern colors, because taste doesn't take a day off just because we're suffering.
The Bracket Analyzer: Snitching on Your Unclosed Curly Braces Since Day One
Ever forgotten to close a {? Of course you have, you're a person who writes code. The bracket analyzer walks your file, tracks a stack of open brackets, and drops a glowing red dot in the gutter the second something doesn't match. It runs 350ms after you stop typing — not on every keystroke — because nobody needs their editor having a nervous breakdown every time they press a key.
The Minimap
A tiny compressed column showing your whole file as 2px colored bars, because sometimes you want to feel like you're piloting a submarine sonar screen instead of writing a Snake clone.
Part 3: The Console (It's Not a <div>, It's a Whole Personality)
The console has color-coded log levels, a real REPL, and message batching so that a game logging 60 times a second doesn't turn your browser tab into a space heater.
The REPL is the fun part: the editor lives in the parent window, the game runs inside an iframe, and we bridge the two dimensions with iframe.contentWindow.eval(). Type player.speedX = 500 into the console mid-game and watch your character rocket off screen like it just heard the ice cream truck. Basically necromancy, but for pixels.
Part 4: The Engine (epic.js) — Small, Mighty, Slightly Feral
Games run on epic.js, the canvas engine we built from scratch, along with the entire editor, the console, the Build tab, and the arcade around it. Two people, one engine, way too many late nights.
The whole thing boils down to one sacred law: every game has an update(dt) function, and if you multiply your movement by dt, your game runs at the same speed whether it's on a gaming rig or a haunted Chromebook from 2013. Frame-rate independence — the closest thing game dev has to a peace treaty.
The New Thing: Teaching Animations to Stop Choking the CPU
Here's the update we're actually proud of this week, and credit where it's due: Kehinde went in and fixed the animation system in epic.js so it stops hammering the CPU into submission. Before this, a busy scene with a lot going on would start visibly dragging — like the game had suddenly remembered it has responsibilities and needed a minute. Now the animation loop is lighter on its feet, the frame pacing holds up under pressure, and games stay smooth even when there's a lot happening on screen at once. No CPU fans spinning up like a jet engine just because someone added a few extra particles.
Part 5: The Build Tab — 15 Templates, Zero Native <select> Elements
There are 15 starter templates — Platformer, Runner, Shooter, Snake, Dungeon Crawler, and eleven of their cousins — each one a JS object that spits out working game code based on a form you fill in.
We also built a custom dropdown instead of using a native <select>, because native selects on Android tablets like to float over your entire app like a UFO that doesn't respect your design system. Rude. Not on our watch.
Color pickers got the same glow-up: instead of the sad native <input type="color">, every color field is a full card with a swatch, a live hex code, and a copy button. If we're building a game IDE, it's going to look like one, not a 2004 HTML form that time forgot.
Part 6 & 7: Command Palette and Find & Replace
Ctrl+K opens a fuzzy-search command palette straight out of VS Code. Type "fmt," get "Format Code." Type "sv," get "Save" and "Save As" fighting for the top spot like siblings arguing over the last slice of pizza. Ctrl+F opens Find & Replace with a match counter, because scrolling through 400 lines hunting for a typo by eye is a form of self-harm we do not endorse.
Part 8, 9 & 10: Cloud Sync, Sharing, and the Arcade
Save your file and it goes to Supabase if you're logged in, or quietly to localStorage if you're not — either way, your work survives, because losing an hour of game-dev progress should be a punishable offense in several countries.
Hit Share and you can either publish your game to the Arcade (the public gallery of everyone's creations) or grab a private link. The Arcade has search, a Featured banner for the week's most-liked game, and auto-generated thumbnails — template games get a stylized scene, custom games get a procedurally chosen art style based on a hash of the game ID. No two games end up looking alike, which is more than we can say for most portfolio sites.
And yes — Desire already told Kehinde he's topping the leaderboard this week. Screenshot pending. Bragging rights secured in advance. If it doesn't happen, this paragraph never existed and you didn't read it.
Part 11, 12 & 13: The Player, Profiles, and the Leaderboard
Every game has its own player page with Like, Fork, Fullscreen, and a comment thread (nested one level deep, because we're not trying to recreate a Reddit thread war inside a game IDE). Every user gets a public profile with an animated rainbow banner that we are, frankly, a little too proud of.
And then there's the weekly leaderboard — a literal podium with a bobbing 👑 on first place, resetting every Monday UTC. Nothing motivates a small team quite like public, weekly, slightly humiliating rankings that reset right when you're finally winning.
Part 14 & 15: Inbox and Settings
The inbox tells you your stats, your leaderboard standing, and any engine updates, so you always know exactly how many people liked your Flappy Bird clone this week (the answer is never enough, there is no number that will satisfy you, this is the curse of shipping things publicly).
Settings let you set a permanent username, a changeable display name, an avatar URL, and subscribe to weekly updates, because everyone deserves the chance to opt back into our nonsense on a weekly cadence.
Part 16: The Database
Games, likes, comments, profiles, follows, saved files, share links, changelog entries — all living in Supabase tables with Row Level Security turned all the way up, because nobody's editing anybody else's stuff except through the front door, with a badge, and an auth.uid() check standing guard.
Part 17 & 18: Design System and Performance
Dark background, blue/purple/pink accents, nothing animates faster than 0.15 seconds because slow motion reads as expensive and we would like to seem expensive. Under the hood: console messages batch per animation frame instead of spamming the DOM 60 times a second, bracket checking is debounced, the console caps at 500 lines, all UI animations freeze the instant a game starts running, and — as of this week, thanks to Kehinde — the epic.js animation loop no longer treats your CPU like a personal enemy. Smooth frames, happy fans, no jet-engine noises from your laptop.
Part 19: Deployment (Or: How We Avoided Docker Like It Owed Us Money)
Static frontend on Vercel. Supabase for the backend. No Docker, no Kubernetes, no CI pipeline beyond "push to git and pray a little." Edit a file, push, it's live. It's the deployment pipeline equivalent of a shrug that somehow always works.
Part 20: What's Next
Multiplayer via Supabase Realtime, sprite and tilemap editors, an audio library, fork trees, playlists, achievements, and scheduled tournaments. Basically, we're not done — we're just done for today.
📋 The "I Don't Want to Read All That" Summary Table
| Feature | What It Actually Does | Why You Should Care |
|---|---|---|
| Single-file editor shell | Whole IDE in one HTML file, no build step | Deploys anywhere, breaks nowhere |
| Custom code editor | Hand-built syntax highlighting, gutter, minimap | Feels like a real IDE, weighs like a text file |
| Auto-indent & bracket matching | Smart {}()[] handling, red-dot error markers |
Fewer "why won't this run" moments |
| Live console + REPL | Real-time eval() into your running game |
Poke your game's guts while it's alive |
| epic.js engine | Lightweight canvas game engine with update(dt)
|
Frame-rate independent games out of the box |
| New: epic.js animation fix (by Kehinde) | Animation loop no longer hogs the CPU | Smooth gameplay even in busy, particle-heavy scenes |
| Build Tab (15 templates) | Form-driven game generator | Go from blank page to playable game in minutes |
Command Palette (Ctrl+K) |
Fuzzy-searchable action menu | VS Code energy, browser-tab price |
Find & Replace (Ctrl+F) |
Search, replace, match counter | Stop scrolling like it's 1998 |
| Cloud sync | Supabase-backed save, falls back to localStorage
|
Never lose your masterpiece |
| Share & Publish | Private links or public Arcade publishing | Show off or keep it secret, your call |
| The Arcade | Public gallery, search, featured games, auto thumbnails | Where games (and egos) go to be discovered |
| Player page | Like, fork, comment, fullscreen | Full game-consumption experience |
| Creator profiles | Rainbow banner, badges, follow system | Internet fame, minus the algorithm |
| Weekly Leaderboard | Podium, crowns, resets every Monday | Wholesome public pressure |
| Inbox & Settings | Notifications, stats, account controls | The boring-but-necessary stuff, handled |
| Performance tricks | Message batching, debounced checks, animation freezing | Smooth even when your game logs like it's angry |
🤝 Need Help, Found a Bug, or Just Want to Say Hi?
We built this thing in public and we'd love for you to poke at it, break it, and tell us about it.
| Where | Link |
|---|---|
| 💬 Discord community | discord.gg/kVpSmYWXr |
| 🧑💻 GitHub | github.com/terracodes004 |
| 📧 Support / general questions | evolvedtech004@gmail.com |
Closing Thoughts
Limn Studio exists because we refused to install a framework and then dared each other to keep going. No npm install, no webpack, no dependency graph plotting against us at 2am. It loads in one HTTP request, works offline after the first visit, and runs on anything with a browser and a pulse.
If you're building something similar: start small, add one feature at a time, and only optimize once you've actually measured something slow. Almost nothing in this project exists because it looked clever. It exists because something was annoyingly slow — like, say, an animation loop dragging our CPU down with it — and we fixed it out of spite.
So go open the editor. Type a few lines. Hit Run. You don't need a gaming rig, a 40GB SDK, or a computer science degree with a minor in suffering. You need a browser, an idea, and the willingness to write one line, watch it run, and write the next one.
Stop waiting for inspiration. Draw your game into existence. 🎮
Limn Studio is live at limn-engine-doc.vercel.app — go break it and tell us how.






Top comments (0)