Lachlan's avatar@lachlanjc/edu
All Physical Computing

Final Project

Decarbonize cover

In the upcoming decades, the US needs to both dramatically grow the capacity of our electric grid while simultaneously transitioning it to carbon-free energy, including solar and wind. In this interactive board game, you’re in the driver’s seat: install electricity to meet demand while keeping carbon emissions and energy prices down.

The game runs on a mounted iPad with the physical board and pieces set in front of the iPad. The board pieces are laser cut cardboard with custom iconography for each of four energy sources: coal, gas, solar, and wind. The bottom/back of each piece has a barcode on it; the iPad scans the barcode as a purchase mechanism (think: store self-checkout). As both supply (electricity sources you install) & demand (automatically calculated to tick up through the game) change, consumer energy prices change. If you fail to grow the grid on pace, the game ends early, but the exercise of choosing the carbon trajectory of your grid lets players see the benefits/consequences of different paths we can choose as a society for our energy infrastructure.

Open the game

If you’re playing without the physical game pieces, use keyboard shortcuts instead of the camera scanning to purchase additional energy sources: a (solar), s (wind), d (coal), f (gas).

Code on GitHub


I demoed to the Physical Computing class! Scanning went much better & people had positive feedback.

I also went to the NYU Game Center’s Playtest Thursday to get feedback and watch how people interact with the game. Nothing like a dose of seeing other people try it to remind you how unplayable your game is. I got a great set of feedback:

  • Label axes of the chart
  • When scan is success, more visual fanfare
  • Sounds are too quiet (& won’t be heard during the show)
  • Chart & numbers overlap; move live numbers to the upper left
  • Is this going well? Hard to tell. Needs clearer goal/objective
  • Expand units, symbolize with electricity icon & thermometer icon
  • Alert users if the camera feed is blurry
  • The first player installed all the renewables & asked why they’d do anything else
  • Ending reflection is needed
  • Lengthen intro; people kept immediately losing. Game feels fast-paced
  • Add nuclear?
  • You run out of game pieces, not board space, so the energy density concept of having larger pieces doesn’t land

I implemented several of these changes during playtesting, and found that e.g. moving the numbers then put them behind the game pieces being held up for scanning. Never a simple solution!

I also had an idea for tomorrow: installing solar & wind is not carbon-free, even if the operation of it is. It’ll make the carbon graph more exciting to get bumps in the renewable installation years.


After the scanning failed on Monday, I entirely rewrote it. I switched off the libraries I had been using to quagga2, written as a custom React hook. It raised the success rate an order of magnitude, so was super worthwhile.


I wrote a reflection on the project so far for my Green World class.

I acquired hand wipes for players to use with the physical coal pieces, which I’m yet to make.


I doubled the number of game pieces, and attached the bar codes. It took way longer than I expected, I did catch one piece of cardboard on fire, and the laser cutter failed to cut out the cardboard pieces, so I painstakingly X-acto knife cut them out manually. Printing out the barcodes and using the laser cutter to cut them out worked seamlessly, except for the code in the upper left of my sheets, which the ventilation system sucked away, requiring me to do several rounds.


The shop ran out of glue sticks, leading me to use tacky glue which took longer and provided more wrinkles. Alas. A full set of game pieces is finished.


Tonight was demo day/play testing day 1! I presented for my Green World class. The demo went well, except the camera-based checkout mechanism fell flat on its face, working less than 10% of the time. This surprised me as the scanning proved reliable during testing, but the game pieces I used for demo were the new batch. The room lighting, size of bar codes, and focus distance of the iPad camera compared to the MacBook I tested on could all have been factors in the failure during the demo. This needs a wholehearted revisit, in whether it’s viable at all and my specific implementation.

People loved the concept, though, and the live interaction. The units were unfamiliar (kWH, tCO2), and evidently too complex as a whole, requesting an onboarding tutorial and an explanation guide. This wasn’t surprising feedback.

Here’s the software as of now.


Called my game designer friend Chris this afternoon for a quick consultation on why this project isn’t compelling. The budget is not a true constraint right now, and the pricing system doesn’t contribute enough to be worth the complexity. He suggested the idea of switching from purchase prices to managing the consumer price of electricity, which I implemented with a deeply basic algorithm.

To make the game more playable in isolation, I added in-game messaging to guide you through various interactions. This makes the onramp easier without a formal level system. Unfortunately, the way I’ve written it has a bunch of state bugs & conflicts I need to work out.

I prepped my Illustrator docs & booked the laser cutter for tomorrow, so I can make more game pieces, print out the barcodes, then laser cut the paper sheets of barcodes to glue onto the backs. By laser cutting the paper, the edges can match precisely, backing the pieces with the white paper, leaving a more consistent appearance.

Barcodes and pieces laid out in Illustrator


Added sounds today, for when additional capacity is needed and when scanning goes successfully, following Josh Comeau’s excellent article & hook. I ran into issues with Zustand’s subscription middleware, so the “additional capacity necessary” sound occasionally fires at the wrong time.


The game is complex yet uncompelling right now—not a great combination. I built out the scanning mechanism today, using react-webcam for the camera feed and zxing for barcode processing. It clicked into place quickly.


I implemented a virtual representation of the board to make testing concepts easier. CSS grid-auto-flow: dense; came in handy!

Virtual board

Pieces in Illustrator


I haven’t found a set of icons covering energy transition-related objects that’s consistent & high-quality, so for this game I’ve made my own. I started by using Bootstrap Icons as a source—one of my favorite open source sets—but for wind, coal, & gas wanted to make custom ones. I’ve done icon design for Supercons before, which taught me a ton (such as how time-consuming & labor-intensive making these simple shapes is), but these needed more custom components instead of recycling existing pieces from other icons. Pretty thrilled with how they turned out, they look somewhat professional!


I went overboard with my implementation of the icons on the site, adding a microinteraction for an animation on hover to the wind turbine. In the process, I leveled up my Tailwind CSS skills. I wrote about the process & technical details on my Notebook.


I try to learn a new library or technology with each project. With the water game, I struggled with state management: a bunch of global JS variables quickly grew intertwined & difficult to reason about. In previous React project’s I’ve always used React’s built-in state/effect hooks for state management, but this project became the perfect time to try something new. I wrote an initial state machine with Zustand. I found the model, both mentally & in code, much clearer than the mix of state & effects the hooks solution would’ve entailed, because it separated actions in game state from how those actions occur, much less UI results of state, which is where I’ve found this get messy in P5 or with plain hooks. Here’s the TypeScript types of the basic state I laid out:

export type SourceName = 'solar' | 'wind' | 'coal' | 'gas'
interface Source {
source: SourceName
price: number
// t2co2e per year
co2Rate: number
// Whether it’s active or decommissioned
active: boolean
// define types for state values and actions separately
type State = {
year: number
budget: number
message: string
installed: Array<Source>
sources: Record<SourceName, Omit<Source, 'source' | 'active'>>
emissions: Record<number, number>
type Actions = {
reset: () => void
tickYear: () => void
isGameOver: () => boolean
getTotalCapacity: () => number
getYearEmissions: () => number
getLifetimeEmissions: () => number
getLifetimeCapacityOfSource: (src: SourceName) => number
purchase: (src: SourceName) => void
decomission: (src: SourceName) => void


I was intending to find gravel to spray-paint gray to serve as coal, but my dad mentioned genuine coal has a unique appearance. I went to Nature’s Cover outside my hometown in central PA to find some. I was shocked coal is just 19¢ per lb—that’s the cost of extraction, at least. (I didn’t even have to pay for my small bag of coal because it’d only be a few cents.) He was right, it looks super cool:


This coal is from Blaschak Anthracite in eastern PA, which I found on a map: