2026-03-01

I built the software side of my alt-control game, Nuclear Logistics Bureau's Typing Challenge, as a website. Rather than use a game engine like Godot, or Love2D, or even Phaser, I used React.

Why.

Yes, very valid question.

  1. Game engines don't really handle typing input easily. Web browsers are built for it.
  2. There's no player, no graphics, no physics. Everything on screen is meant to simulate an ancient computer terminal. Would would using a game engine give me? I thought about using, say, Twine, because my game is essentially branching text nodes, but I don't think Twine is set up to handle typing input either. Rather than try to learn a new tool that may or may not do what I wanted, I figured it would be easier to just use the tech I know. And the biggest reason:
  3. After being out of work for a few months, I start a new job as a front end engineer soon and I thought this would be a great way to derust.

And I think it was!

Some notes about the structure

  • This game is a bit like a piece of interactive fiction (see my internal debate about using Twine above): there's a prompt on the screen, the player types a response, and a new prompt appears based on that response. That's not solely how it goes; you can play tic tac toe (the CPU is real dumb, like dumb as rocks), and you can play the eponymous typing challenge, and those both have a bit more complicated state. Oh, and there's sometimes (usually) a countdown timer. There are 8 possible endings.

There weren't THAT many nodes (65), I kept all the prompts in one dictionary of PromptID: {Prompt}.

type Prompt = {
  text: string;
  time: number; //seconds until timeout, or -1 if infinite
  gotos: Object; // ok I didn't name this great. But it's an object where the keys are responses and values are the prompt IDs those responses lead to. For example, {"y": 5, "n": 6}.
  on_unknown: number; // a prompt ID. If you type anything not in "gotos", or if your timer runs out, this is where you go next
  ending_number?: number; // if this is truthy, the program knows the game is over
};

  • I used a Context + reducer pattern and probably 3/4 of the whole game lives in the reducer. This was one thousand percent the right way to build this. Highly recommend if you're doing something similar. I only ended up needing 4 actions, which feels kinda wild.
export enum ACTIONS {
  END_CPU_TURN, //when playing tic tac toe. After the player makes a move, the CPU has to "think" a minute and make its move before returing control to the player
  KEY_INPUT, //dealing with key input was kinda gnarly. Yeah, even after I said all that nice stuff about web browsers. It's because I was doing silly things like silently collecting input until I got a string of length 5, then mapping that input to my ridiculous 5-bit-pseudo-ASCII, oh but also there's a shift key
  PROCESSING_COMPLETE, //after the player types enter, a "processing" "animation" plays, and when it's over, the reducer has to figure out where the player goes next
  TIME_UP, //signaling the reducer to go ahead and move on when the player doesn't hit enter before the timer reaches 0
}

Some things I learned

  • The Web Audio API can generate sounds programmatically. It felt a bit like making SFX in Pico-8, except in JavaScript. Like, you can configure it to play at a frequency, for a duration, using a certain kind of wave, and the web browser will happily beep and boop at you! I had no idea. This is not really the kind of thing that's useful at my day job. It's also probably not a great way to write a soundtrack, but all I wanted was to emit vintage-sounding error beeps, and this was absolutely perfect for that.

  • The browser also has an API for text to speech! I had no idea. I seriously thought about using it, you know, to really drive home the War Games aesthetic, but I ended up cutting it. Mostly I didn't want to spend time and effort wiring it up for a game that's meant to be played in a showcase/arcade environment, where it's hard to hear anything at all.

  • It is shockingly easy to create a "good enough" CRT effect in CSS. No scanlines, but I don't think they're necessary. All you do is add a reddish shadow to one side of the text and a bluish shadow to the other side of the text. It's just enough to make it look a little blurry. (My eyeglasses do the same thing. Please tell me I'm not the only one who sees this.) I normally think green or amber when I think of old monitors (amber monitors my beloved), but cyan is what they used in War Games.

body {
    background: #1b1b1b; /*  black */
    text-shadow:
        -2px 0px 2px #db7497, /* pink */
         2px 0px 2px #41bcbc; /* this is kind of a windows 95 background teal */
    color: #5fe4e4; /* cyan */
}

I should maybe specify that I wasn't trying to make a game about the movie War Games, I just wanted to borrow the vibes.

But is the game actually fun?

Unknown. It'll have its first real playtesters at the showcase event next week. But I think it at least has the potential to be fun!