What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

You can build a three.js real-time strategy game as plain ES modules with no bundler and no framework, and the development loop stays very short. The harder work comes after the first run: keeping simulation logic testable, stopping returning players from loading mismatched JavaScript after a deploy, and choosing input controls people can discover. The developer behind Singularity Rush, a free browser RTS about fictional AI labs racing to train a model, documented all three in a project retrospective published September 28, 2026. This article walks through those decisions, what each one cost, and what the author’s own measurements do and do not establish.

What the project looked like

The game is roughly 6,700 lines of plain JavaScript, organized as a folder of ES modules. An import map in index.html tells the browser where to resolve module names, and the three.js library (vendored at r186) sits in the repository rather than being installed through a package manager. Models come from Kenney under CC0. The development loop had no framework and no bundler: edit a file, reload the page, and the browser runs the repository code directly.

That simplicity is the author’s main argument for the setup. Nothing needs to compile before a change appears, and debugging happens against the same files that ship. The trade-off shows up later, at deployment, and is covered below.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep the simulation independent of the renderer

The central architecture decision was separating simulation from rendering. Simulation state does not depend on the renderer, so the game logic can run without a canvas or a WebGL context. The simulation advances in fixed ticks at 10 Hz, while the renderer runs at the display’s refresh rate and interpolates between the last two simulation states to draw smooth motion.

Two more rules support that separation:

  • Seeded randomness. The random number generator is a seeded PRNG stored on the world object. Nothing in the simulation calls an unseeded random source, so the same seed and the same inputs produce the same outcome.
  • Commands, not direct mutation. Player and AI actions are queued as commands and applied on the next simulation tick. The input layer never edits game state directly, which keeps every change in one ordered path.

The author guards this boundary with a source check. The check rejects three.js imports, references to browser globals, and unseeded randomness anywhere in the simulation directory. If someone accidentally imports the renderer into game logic, the check fails before the code is ever run.

What the separation made possible in testing

Because the simulation has no browser dependency, the author could run complete AI-versus-AI matches under Node in continuous integration. The checks described were:

  • every match finishes with a winner;
  • stockpiles never go negative;
  • repeated runs with the same seed produce matching world hashes.

The hash comparison is the most useful of these for a game with a simulation at its core. A single changed line that alters timing or ordering shows up as a different hash, even when the match still looks plausible on screen. The author also added a ?skip=600 URL parameter that fast-forwards AI play, which is handy for producing screenshots without waiting through a full match.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

These CI results are the author’s own reported checks on one project. They show that the approach works for this codebase; they are not a benchmark of the technique in general.

Rank #2
Hedlaoy 32000 Games 3D Pandora Box Arcade Game Console,Retro Video Game Machine for TV, 1280x720 HD Output,Search/Save/Hide/Pause Games,Support 4 Players,Two Splited Joysticks.
  • 32000 Games Updated System - This Pandora arcade console comes preloaded with 32000 HD games, including 161 3D titles. It offers a diverse selection of classic games suitable for a wide range of ages, features mainstream emulators to meet the needs of various players.
  • Upgraded Hardware & HDMI Output - The Pandora Console is equipped with an enhanced game chip featuring a 8-core CPU and 32GB of RAM, ensuring stable and smooth system performance. It supports 1280x720 full HD output, providing a high-definition gaming visual experience.
  • Wide Applications - This Console is widely suitable to connect with TV set/PC/Xbox 360/PS3/Screen/Monitor/projector. Just plug and play games!Connecting with 2 controllers, you can enjoy 3-4 players games together.
  • Smart Functions - The 9-grid search function enables users to easily locate the desired games. Additional features include a favorites list, a recently played games list, the ability to pause/save/resume games, customizable buttons, and one-key language switch.etc
  • Separated Joysticks -The design of two joysticks separated make 2 players have enough space playing together. One players can take the P1 joystick to play games only, no need of P2.

The caching problem that appeared after launch

The author configured the origin server to send Cache-Control: no-cache for code files, which should force browsers to revalidate. The problem was a CDN setting. A CDN “Browser Cache TTL” rule overrode the origin header for JavaScript and CSS files. In the incident the author describes, the response for main.js carried max-age=14400, which is four hours in seconds.

The result was a version mismatch. A player who had visited before a deploy could load a new entry module while still running an older HUD module cached from the previous release. Nothing in the source files was wrong; the browser was combining two versions of the code.

The fix: content-hashed asset paths

The project now runs a post-build step that:

  1. computes a hash of the source files, the vendored three.js, and the CSS;
  2. places the built assets under a directory named by that hash;
  3. rewrites the paths in the HTML and the import map to point at the hashed directory;
  4. checks that no unhashed code paths remain in the output.

Because each release’s files live at new URLs, a cached copy of an old file can no longer be paired with a new one. The author treats this as a deployment step, not a cache-header fix, which is the reason the hashing exists.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Check the headers the server actually sends

The author now verifies production responses directly with curl -I against the deployed URL, rather than trusting the configuration file. The line that captures the lesson is the author’s own: “I don’t trust the config file on this one anymore. I check the production headers with curl -I.” Run the command against your own host and compare the Cache-Control header with what your config says. Any CDN layer between the origin and the browser can change that value, and the origin’s configuration will not show it.

Rank #3
KIZONLY 3D Rolling Ball Puzzle Maze Cube - 3x3 Color-Matched Game
  • Pocket-Sized Rotate & Roll Fun: This 3d rolling ball maze cube is a take-anywhere fidget toy, small enough at 2.67 in to slip into any bag. No batteries, no noise. Just rotate, turn and roll the ball through a fun 3D world, turning restless waiting into quiet play wherever you are
  • Two Ways to Play: One see-through cube, two diverse games. Maze Mode: move and turn to steer the ball along slopes and turns. Color Mode: turn the puzzle until each face shows the same orange or green tiles. Sharpens spatial awareness, logical thinking, and coordination
  • See-Through & Sturdy: A see-through cube lets you watch every smooth roll of the ball. Polished rounded edges and a completely enclosed ball make it safe for ages 3+. Sturdy enough to handle frequent collisions and tumbles. The small size suits little hands and makes it easy to take along
  • Alone or with Friends: Roll alone and let stress ease with each gentle turn, or begin a friendly game. See who finishes first, take turns, or pass it around. Unlike 2D puzzles, the layered 3D design adds extra fun and a rewarding finish on every course
  • Gift for All Ages: A good birthday or holiday present for kids 3+ and adults. Use as a desk toy, portable game, or family night fun. Suitable for quiet time at home, in the car, or waiting areas. Browse our race cubes and 3D ball puzzle series

Header behavior depends on your hosting and CDN settings. The author’s observation applies to that setup, and your provider’s defaults may differ.

Showing units hidden behind buildings

At the default camera angle, buildings and trees covered the heads of between 11% and 23% of on-screen units, according to the author’s measurement. In an RTS, a unit that disappears behind a structure can look as if it has been removed from the battle, so the author treated the problem as a gameplay issue as well as a rendering one.

The proposed fix works in two passes:

  • Normal pass. The scene renders as usual, and the visible unit pixels are written to the stencil buffer.
  • Silhouette pass. An animated team-colored silhouette is drawn where a unit is occluded, using the stencil mask to limit it to the hidden portions.

The technique depends on the renderer being created with stencil support enabled, which means passing stencil: true when the renderer is initialized. Without that flag the stencil buffer is not available and the silhouette pass cannot run. The article describes this as the author’s planned approach; it does not report measured performance costs for it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choosing mouse and touch controls

Input was the author’s least settled area, and the final choice reflects a deliberate comparison. The author reviewed more than 20 desktop RTS games to see which conventions players already expect, and then settled on this scheme:

Input Desktop Touch
Single drag Left-drag draws a box selection Drag pans the camera
Other drags Right-drag or middle-drag pans the camera Not applicable
Select on touch Not applicable Long press, then drag, to select

The author found that left-drag doing double duty, sometimes panning and sometimes selecting, was confusing. Reserving left-drag for selection matches what most RTS players already expect, and moving panning to the right or middle button keeps the camera reachable without a modifier key. On touch screens, where there is no second button, the long-press-then-drag gesture separates selecting from panning.

The survey of 20-plus games is the author’s own account of which conventions they reviewed. It is not an independent study of the genre, so treat it as one developer’s reasoning rather than a settled industry standard.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Weighing the three trade-offs

Each of the project’s main decisions gained something and cost something. The table below lists them as the author describes them.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Decision What it gained What it cost
No bundler, plain ES modules Edit, reload, and see the change; debugging runs the shipped code Asset versioning and cache consistency had to be handled deliberately at deploy time
Simulation separated from rendering Headless AI matches, world-hash comparison, and a source check that keeps browser APIs out Extra structure: commands, fixed ticks, interpolation in the renderer
Left-drag selects, right or middle drag pans Matches common RTS expectations and avoids mode confusion Touch needs a separate long-press gesture to select

Scope of these findings

Everything in this article comes from one project, described by its developer in a retrospective published September 28, 2026, and republished on DEV Community with the author credited as Nick. The original page does not give a fuller name or formal role. The figures (6,700 lines, 10 Hz ticks, the 11% to 23% occlusion range, and the 14,400-second cache header) are self-reported. The article does not cite an external study or an independently reproduced benchmark for any of them.

Several claims that appeared in discussion of the project were not confirmed in the original page, including details about trackpad detection, iframe wheel handling, iPhone behavior, game balance, and AI coding tools. They are left out here for that reason.

To read the original retrospective in full, look for Ileventech’s September 28, 2026 post; the author’s exact wording and code samples are there.

”

The Bottom Line

A no-bundler, plain-module three.js project is a workable choice for a browser RTS, provided you plan for two things from the start: a simulation that does not depend on the renderer, and a deployment step that hashes asset paths so returning players never mix code versions. Verify your production headers yourself, because a CDN can override the cache settings in your origin configuration.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.