Building a 2 Player Dino Run Clone From Scratch
Two weeks ago I was trying to get my kids off the iPad during a road trip and remembered the Chrome dino game exists. Problem was, only one person could play it at a time. So I spent a Friday afternoon writing a local multiplayer version where one player uses WASD and the other uses arrow keys to control separate dinosaurs across the same endless desert. The idea sounds simple, but there are real design decisions that trip people up if you haven't thought through the multiplayer layer before you write a single line of code. I'll walk through how I built it, the technical gotchas, and what actually works in practice.
How 2 Player Dino Run Actually Works
At the core, it is the same as the original: a game loop runs at 60 frames per second, obstacles spawn at the right edge and move left, gravity pulls the dino down when in the air, and jumping applies an upward velocity. The change is that now you have two dino objects instead of one, two sets of input listening simultaneously, and a single collision check that runs against both. Here is the structure I used. It is not fancy. It is a single HTML file with embedded JavaScript, so anyone can open it in a browser without installing anything. I chose vanilla JS because adding a framework just to make a platformer like this adds unnecessary complexity and slows development down significantly. The canvas size I settled on was 800 by 250 pixels. Anything smaller makes the two dinos feel cramped. Anything larger and the background scrolling feels too slow relative to the obstacles, which ruins the timing. The dino sprite is just a 44 by 47 pixel rectangle drawn with basic shapes. I did not bother with an actual image because the original chrome game does the same thing, and loading sprites just to explain a tutorial feels like padding.
For the input handling, I maintain a key state object that tracks whether each relevant key is currently held down. When a keydown event fires, I set the corresponding flag to true. On keyup, I set it to false. This avoids the stutter that happens when operating systems delay repeated key events. Without this, the second player's jumps feel laggy and unresponsive, which makes the game basically unplayable for competitive co-op.
Get the Full Details

The Implementation Breakdown
The game loop uses requestAnimationFrame rather than setInterval. setInterval drifts over time and produces inconsistent frame pacing, which becomes obvious when you are trying to judge jump timing in a fast-paced game. requestAnimationFrame gives you a callback roughly every 16 milliseconds on a standard display, and it pauses automatically when the browser tab is not active, saving CPU and battery. Inside the loop, I update each dino independently. Dino A responds to W for jump and S for ducking. Dino B responds to the up arrow for jump and the down arrow for duck. Both dinos share the same gravity constant, which is around 0.6 pixels per frame squared in my implementation. The ground level is fixed at 200 pixels from the top of the canvas. When a dino's y position reaches the ground, velocity is set to zero and the dino is marked as grounded. Obstacles spawn at intervals that increase slightly as the score goes up. In the single player version, the original game uses a random timer between roughly 50 and 110 frames. For two players, I shortened that range to 40 to 80 frames because having two avatars means collisions happen twice as often. If I left the spawn rate the same, the game would feel too easy for two people sharing the screen.
The collision detection is axis-aligned bounding box overlap. Each dino has a hitbox that is slightly smaller than the sprite itself. I shrink it by about 6 pixels on each side because exact pixel-perfect collision feels unfair even when it is technically accurate. When either dino collides with an obstacle, that dino stops, the game records the distance traveled by the surviving dino, and the round ends. One detail people often overlook is that both dinos share the same obstacle array. If each dino had its own independent obstacle stream, the gameplay becomes chaotic because you end up with cacti appearing at different positions for each player. Sharing one obstacle pool means both players react to the same threats, which is much closer to what people actually expect from a 2 player dino run experience.
A Real Problem I Hit During Development
Halfway through building this, I ran into an issue where the second player's jump input would sometimes register as two separate jumps if they tapped the key quickly. This happened because the keyup event would fire between two keydown events during a fast double-tap, causing the grounded flag to flip back to true mid-air. The dino would then jump again before landing, producing a double jump that breaks the intended mechanics. The fix was straightforward once I identified it. I added a cooldown check that prevents a jump from executing unless the dino is actually grounded and at least 150 milliseconds have passed since the last jump input. This value came from trial and error. Lower and the problem returns. Higher and the controls feel sluggish. I wrote a small timer using the performance.now() API to track this, resetting it whenever the dino lands back on the ground. This edge case only showed up when I actually got someone else to play it. Testing alone with my own fingers on one keyboard is not sufficient because you do not naturally double tap keys the way another person might when competing against your sibling. Multiplayer reveals timing bugs that single player testing never exposes.

Common Mistakes That Ruin the Experience
The biggest mistake I see when people try to build their own version is making both players control the same dino. That is not a 2 player dino run. That is just a local co-op input system masquerading as a multiplayer game. If both sets of keys move one object, you have not solved anything. Each player needs their own avatar, their own physics state, and their own collision checks. Another mistake is not scaling the difficulty for two players. The original game gets harder by increasing speed gradually. With two dinos on screen, you should also consider adding more obstacle types. I introduced flying pterodactyls at higher scores, which forces players to duck while also watching for ground hazards. Single player you mostly ignore the air threats until they become common. Two player you cannot afford to ignore them at all because one missed decision ends the round for everyone. A third issue is screen space. Some implementations I have seen put both dinos stacked vertically on the same canvas, which compresses the gameplay area and makes timing nearly impossible. Keeping them side by side horizontally or one slightly above the other on a taller canvas works far better. My final layout had both dinos at the same ground level but separated by about 150 pixels horizontally, giving each player their own visible lane while keeping both on screen.
2 Player Dino Run Download and Setup
There is no official downloadable binary for this because the most common implementations I know about are open source projects hosted on GitHub. If you want to use someone else's version, search for "2 player chrome dino multiplayer github" and you will find several working repos. The simplest ones are single-file HTML projects that run directly in the browser. If you want to host it yourself for a LAN game or share it with friends, you just need to serve the HTML file over any local web server. Python has a built-in module for this. Running python -m http.server 8000 in the folder containing your HTML file lets you open http://localhost:8000/dino.html in any browser on your network. No installation, no compilation, no build step. For people who want to modify the code themselves, the entire project I described above can fit in a single HTML file under 400 lines. The JavaScript handles all the game logic. The CSS is minimal and only adjusts the canvas centering on the page. The HTML part is just a canvas element and a couple of divs for the score display.
What This Approach Does Not Handle Well
I should mention the limitations upfront because people often assume this is a complete solution. A browser-based HTML canvas game has significant constraints. There is no save system. There is no online multiplayer. If the tab loses focus, the game loop pauses entirely due to how requestAnimationFrame behaves. Network latency is not a factor here because both players share a single keyboard, but if you later want to extend this to remote multiplayer, you would need to completely rethink the architecture and add a server component. The visual fidelity is intentionally low. The dinos are rectangles. The obstacles are rectangles. The ground is a line. This is fine for a quick project or a coding exercise, but if you want actual sprites or smooth animations, you need to add image loading, sprite sheets, and animation frame management, which increases the codebase significantly and introduces asset dependency issues that were not present in the basic version. Performance is generally not a problem on modern hardware because the canvas rendering is simple enough that even integrated graphics handle it without issue. However, if you add particle effects, complex backgrounds, or run the game inside a heavily loaded browser with many other tabs open, you may notice frame drops that affect gameplay feel. This is a browser limitation, not a code limitation, and it is worth testing on the actual devices your players will use before distributing the game.

The keyboard-only input is both a strength and a weakness. It works perfectly for two people at the same computer. It does not work if either player wants to use a controller. Adding gamepad support requires using the Gamepad API, which is not consistent across browsers and operating systems. I tried it once and spent more time debugging the API quirks than actually improving the game. If controller support is important to you, budget extra time for that integration before starting the project.