Past broadcasts

Session 69

Graphics Yes Billionaires No

This is one of those sessions where the look of the game changes a lot in a short period of time. We move a bunch of our graphical experiments into the main game program, most notably the “wiresolid” object renderer. We also throw in high-DPI rendering and scroll-wheel zooming. On a lark, we happily discover that the multiplayer implementation still works. Notes/highlights Carbon API, a venerable framework for building Mac applications: https://en.

See more →

Session 68

The Collector

Join us for an exploration of future gameplay ideas, before diving back into our shader exploits. As much as we love “steve shading”, we’re now calling our method the “wiresolid” shader. We walk through the pros and cons of the different techniques we’ve been experimenting with, and add a few additional tweaks – such as anti-aliasing! We learn a great deal about our screen-space derivatives for edges, and found even more OpenGL features that we wish we could use but that aren’t available in WebGL.

See more →

Session 67

The Wireframe Wild West

The saga of wireframe rendering continues. We begin by expanding the capabilities of Rendertoy, our 3D visualization tool, so it can use different rendering modes. This allows us to compare the results of two different rendering techniques. The first is a two-pass method that renders a line mesh on top of an opaque triangle mesh. The second is what we’ve come to refer to as “Steve shading”, a one-pass method that does tricky math in the fragment shader.

See more →

Session 66


We start today by walking through a refactor of our GLTF model loader, which now preserves our model data after load for future processing in JS. After our last stream with Steve Foot, he came back to us with a really clever idea to use the fragment shader to eliminate triangle edges by adding a single attribute to our vertices. We talk through this algorithm, and how our new loader can help support it, before diving into some code.

See more →

Session 65

Synthwave is our Bob Dylan

Today, we’re once again joined by Stephen Foot, previously seen in Session 53 helping us out with our networking layer. Today, Steve does a deep dive with us on our OpenGL stack, and helps us brainstorm our wireframe rendering techniques. At the end of the day, Steve left us with a question-slash-mantra for graphics thinking: “are there additional attributes that I can encode?”. See our previous session with Steve here: https://jeffanddom.

See more →

Session 64


Our quest for a wireframe renderer continues. We begin by examining Rendertoy, a tool Jeff made for visualizing renderer output that would be a lot more useful were it not for a nasty gimbal lock bug. After staring mouth-agape at whiteboard math for a while, we make a tactical retreat into the fragment shader, where we hope to find a way to remove diagonal lines across flat surfaces. We quickly realize that the fragment shader can’t do this job without some extra information, so we run an experiment to see if adding an “erasure” vertex attribute will help.

See more →

Session 63

Wireframes and Plotlines

Now that we’ve got the most basic 3D objects drawing on screen, we commence an ill-advised charge directly toward a synthwave aesthetic of which the zeitgeist is rapidly tiring. First on the list is wireframe shading. How the hell do you do it? First, we study how to get the fragment shader to do lines, which requires adding barycentric coordinates to our vertex attributes. It turns out to be easier to implement than pronounce, although we still manage to trip over the little things.

See more →

Session 62

2020 Reviews + Grid Shaders

Welcome to 2021. Let’s pretend Dom wrote these notes before the attempted overthrow of our government, and that everything is fiiiiine out there. We start the year looking back at some of our favorite games from last year, and then exploring how to craft a wireframe shader. Our notable games from 2020: Hades (J + D): gosh we both love it. Hades manages to bridge the gap between Dom’s love of rogue-likes, and Jeff’s equal dislike of them.

See more →

Session 61


Our modeling system continues to progress! After working with the limitations of OBJ models, we decided to switch our model format to glTF (GL Transmission Format). We like this format a lot - today, it makes it easier for us to address and animate parts of the model with a predictable hierarchy, and in the future provides support for a lot of advanced modeling features. To start the day, we tracked down a bug in our new glTF render pipeline and got our tank rendering from the new format.

See more →

Session 60


We recap a bunch of new features and tweaks that got added during our weeklong vacation away from the stream: there’s a new shader and interface for handling 3D debug draw, a few of the most annoying bugs have gone away, and the code got refactored a bit for ease of use. Next, we decide to try adding lighting, after spending several sessions talking about it but not actually doing it.

See more →

Session 59

Tightening up the graphics

We begin with a demonstration of our nifty new cloud development setup, which lets us build the game and run the game server using cloud-based computers. Then we attempt to add 3D lighting into the game, which necessitates reading vertex normals from OBJ files. A bungling misadventure in refactoring unfolds, leaving the game looking slightly more broken than before. But it turns out the real treasure is the conversations we had along the way.

See more →

Session 58

Sometimes the Model Flips You

Dom reports that he was able to sneak out of with a PS5, and is now enjoying his last-gen games in sweet, sweet 60FPS. He then unveils his latest work, which lets us load OBJ files and draw actual 3D models into the game. Soon, the game is filled with life-like trees and tanks. We quickly discover there is no agreed-upon standard for orienting 3D models, we spend the rest of our time flipping and rotating objects so they draw correctly.

See more →

Session 57

We're 3D at Last

Manchester finally makes its leap into 3D graphics. Last time, we had a renderer working with 3D primitives, but it generated a firmly two-dimensional result. This session, we move the camera into 3D space, and give it a tilt to emphasize the perspective effect. We also get our entities drawing again, and most important of all, we fix the garish terrain colors. Notes/highlights - Quaternion, an expanded complex number used for representing rotations and orientations: https://en.

See more →

Session 56

OpenGL: The Awakening

We pull all the wires out of Manchester’s 2D renderer, in the hopes of replacing it with a shiny new OpenGL replacement. There are many blank stares as we try to remember how matrix multiplication works. To our shared surprise, we’re able to get terrain rendering working, although something about the colors seems a bit off… Notes/highlights A nice explainer on the difference between normalized device coordinates and other coordinate systems: https://computergraphics.

See more →

Session 55

A Matter of Perspective

Returning to OpenGL, we dive into perspective rendering. Previously, we’ve rendered a single square in a flat plane, but to draw a scene that looks three dimensional to our brains, we’ll need to get more complex and start mimicking how our eyeballs perceive the world. After going through the math, we sketched out a clean-room implementation for drawing a cube in perspective based on the WebGL2 Fundamentals examples. Stick around to the end of the video to see one heck of a red-and-black spinning cube.

See more →

Session 54


We walk through the WebGL 2 Fundamentals state diagram, an interactive tool that will help to demystify the OpenGL API. Nobody would ever accuse OpenGL of being intuitive or easy to use! We walk through an example that draws a colored triangle, truly the “Hello, world!” of graphics programs, and then try a more complex example that uses perspective projection, texture mapping, and diffuse lighting. Notes/highlights: Here’s a link to the WebGL 2 Fundamentals state diagram tool: https://webgl2fundamentals.

See more →

Session 53

Feat. Stephen Foot

We think you’ll learn a ton and have fun with this one; we certainly did! This is our first-ever three person stream, with guest Stephen Foot dropping knowledge gained across 15 years in the game industry. We play Manchester with 3 players for the first time ever (there are, aheam, some kinks to work out). Then we review our multiplayer architecture and ask Steve for thoughts and suggestions. Of course, it turns out he has studied everything we have, and more.

See more →

Session 52

Fine Art

Our blind charge into the world of 3D graphics continues! This session we grapple with the classic game developer’s rite of passage: staring for two hours at a sea of magenta, trying to get a square with gradient colors to draw on top of it. Both of us proceed to badly misremember the lessons of past graphics classes and projects. OpenGL’s brutal ergonomics offer no extra assistance; we arrive, as many before us have, at the conclusion that the API is bad.

See more →

Session 51

Another Dimension, Another Dimension

We’re doing it. It’s finally time to move our renderer from 2D to 3D (and take a short break from working on our netcode). We’re still going to be using the Canvas element, but switching the rendering mode to use WebGL2. As we dug in, we found that some parts were similar, and – as expected – some that weren’t. After updating our screen clearing function, we looked at the changes that would be required for our camera and renderable systems.

See more →

Session 50

Hades Racing

This is our 50th Twitch stream since we started this project. Five-Zero! Wow!! We’re having such a fun time, and here’s to 50 more ✨ For the first hour, we continued our move to immutable objects in our game systems and applied our ComponentTable pattern to our damageable objects. In the process, we had to refactor both our Damageable and Hitbox classes to be pure data types instead. We followed the Typescript errors and were able to refactor cleanly…but only after touching nearly ever entity!

See more →

Session 49

Four Seasons Immutable Entities

Forgive some audio issues at the beginning of the stream! OBS gonna OBS. In this session, we started sending mouse position in our multiplayer events to allow players to see where other players are aiming in realtime. We started playing the game together, and at a buttery smooth almost-60 FPS. Our hiding system got an upgrade to hide tanks from other players as well as enemy turrets. For the second half, we dug into our twin implementations of immutability.

See more →

Session 48

Ghosts in the Machine

Dom returned to the stream after a hard weekend of public service, only to find that the game no longer has a entity class. Instead, we’ve got a bunch of maps and sets, keyed by entity ID. It sures seems like you can take the web programmers away from the web, but you can’t take the web out of the web programmers. After that, we finally turned on some old systems like turrets, but have to deal with the perennial problems: bad synchronization logic and poor performance.

See more →

Session 47

“Build Back Better” is hard to say

With the election right around the corner, we’ve got politics on the mind. Expect some anxiety over bleak outcomes and lots of armchair philosophizing about the possibilities of democracy. But we were able to cram at least a little programming into the stream. We add some new container classes, true catnip for the programmer trying to procrastinate while avoiding the hard problems. We also got rid of some redundant particle emitters, which was a nice frame rate optimization.

See more →

Session 46

Always Index the DB

After reviewing Jeff’s latest type and prediction updates, we continued to pick away at simulation performance. With some digging, we spent some time indexing our entities by their individual component types. We eliminated ~30k iterations through the entity manager during re-simulation by looking entities up by component type, and saw our frame rate start to climb. We’re using an “opaque type alias” to keep our entity IDs typechecked. Since Typescript doesn’t have built-in support for opaque types, we simulate it with a clever intersection of an impossible-to-satisfy type signature: https://codemix.

See more →

Session 45

Vote No on California Prop 22

As we continue to integrate spatial indexing into the game, we take a couple of field trips. First, we refine some function names that make our multiplayer prediction system much easier to understand. Naming things is hard! Then, we take another shot at improving our frame rate issues, this time looking into how our background terrain gets drawn. Notes/highlights: This is an important election for many reasons. If you’re a voter in California, please consider voting against Prop 22, which would dismantle hard-won protections for gig workers: https://www.

See more →

Session 44

Purgatory is a Quadtree

In between trading tips about Hades (one of the best games of the year!), we continue plugging away at our spatial partitioning system. Notes/highlights: Paradise Killer, a vaporwave detective game that’s been making the rounds: Ever have a program that uses the same data type for different concepts, like currencies and measurements, and wished the typechecker could tell them a part? That’s called an “opaque data type”, which some languages support, but not TypeScript.

See more →

Session 43

Blame Infinite Recursion

We plug our shiny new quadtree code into our collision system, only to run into an infinite recursion bug. Boy do I hate those! Notes/highlights Hades is the game Dom and I both playing right now. It’s great! Coinbase, a company of dubious social value, has adopted the dubious policy of no politics in the workplace: The “pimpl” design pattern, a C++ technique for hiding implementation details: https://en.

See more →

Session 42


After our successful multiplayer trials, we considered updating more of our existing gameplay for netplay. However, it was clear that we had a growing performance problem in some of our systems that required a deeper fix. Enter the Quadtree: a data structure that provides logarithmic-time lookup for an entity in our map based on its position. We implemented and tested multiple subroutines for the tree, and are excited to put all the pieces together in our next session.

See more →

Session 41

Awkward Simulations

The day has arrived. Finally – we can play a game together and blow up the other tank! It’s a little tricky to aim, as we’re always shooting at where the other player was instead of where they are, but we’ll start getting into the prediction business sometime soon. After adding OVERDRIVE mode last week to help clients catch up to the server, this week we added a slowdown message to bring clients back to normal speed.

See more →

Session 39

Dependency Management

Okay, it’s been long enough: we want a real server process, not a fake one running in the browser. To get there, we have to fight through a mess of confounding issues: how to get both the in-browser client and server-side process to restart, and how to get them to talk using WebSockets. Along the way, we run into not one but several false starts using off-the-shelf libraries. Notes/highlights “The Case of the Missing Hit”, a all-time great podcast from Reply-All, about a song someone swears they remember but doesn’t actually seem to exist: https://gimletmedia.

See more →

Session 40

Time Dilation

This session marks the first appearance of “true” multiplayer in Manchester, with the game running on three different processes. It’s the first time Jeff and Dom have played the game at the same time! Of course, we run right away into a tricky bug: the game stops working if one client runs even slightly faster than the other, which means we need to compensate somehow. This is an interesting problem, but we all know you’ll watch this episode because it has cats and dogs in it.

See more →

Session 38

War with the Machines

As we gain confidence in our multiplayer implementation, we start to flip the lights back on for several other gameplay systems. Bullets move again, and the player can’t drive through walls anymore! But new systems mean new problems. We wrack our brains to figure out a mysterious collision issue, and begin to suspect our client-side prediction system is getting too expensive. Notes/highlights You can’t use UDP in the browser! Here’s netplay guru Glenn Fielder’s exploration of this tricky limitation for games running the browser: https://www.

See more →

Session 37

Sprinkles and Jimmies

We factor out a common simulation abstraction for the 3 or 4 places where we simulate the game: server, client reconciliation, and client prediction (twice). To combat the performance issues caused by duplicating our game state too many times, we added checkpointing to our entity management, reducing clones in exchange for state-handling complexity: now we have to mark objects as dirty before modifying them. Finally, we re-enabled the shooting system…but not our bullet physics.

See more →

Session 36

Prediction is You

We added a second simulated tank with a simulated keyboard input, and the beginnings of a world with multiple players in it. En route to that, we fixed a bug where we weren’t actually processing server messages. This all reintroduced serious performance problems…we sense optimization in our future. Notes/highlights Working Effectively with Legacy Code Alan Wake: a cool “old” game: Remnant from the Ashes: good if you liked Destiny or Dark Souls: https://en.

See more →

Session 35

Your FPS Display Needs a Bigger Font

We fixed some gnarly frame rate issues! To help with the optimization, we built an FPS display into the debug overlay, which ended up being so cool that maybe it should just be the whole game. Notes/highlights: The new website is done-ish. Check it out at Nice work, Dom! We took our inspiration from the character select screen from Turtles in Time ( The game is now code-named “Manchester”.

See more →

Session 34

Simulated simulations

Our goal today was to attach multiple clients to the server. Instantiating multiple clients means we had to split the game object into “client” and “server” portions, a change that we started working on last stream. Along the way, we encountered a significant slowdown when running a second client loop, which means some optimization is due. Notes/highlights “1500 Arches on a 28.8”, a classic article about the low-bandwidth multiplayer implementation in Age of Empires 8-Bit Game Graphics Converter, a website that will downres your photos with retro style We teased our website!

See more →

Session 33

Client-side prediction

We get the game (or at least player movement) working smoothly again with a minimal implementation of client-side prediction.

See more →

Session 32

Server-to-client communication

We continue to build up netplay foundations. This time, we start to move away from a shared-memory model for sending state from server to client.

See more →