PHIG: A Platform for Hosting Infinite Generativity

PHIG: A Platform for Hosting Infinite Generativity

A modular, real-time multiplayer, language-based game platform and bootcamp final project.

The Infinifig, mascot of The Platform for Hosting Infinite Generativity.


By the time I got to the end of my time at Lighthouse Labs’ Web Development bootcamp, I’d been exposed to the exciting possibilities of websockets for multi-user simultaneity in a chat context and was dreaming up silly applications like programmatically enforce participants to write in iambic pentameter with syllable-checking libraries. My educational background was in English literature, my drive was for building teaching tools, and my inclination was to create some sort of game that could combine all of these things. Gratifyingly, I was able to find two cohort-mates who were at least mildly entertained by my vision and were willing to collaborate.

We became The Standard Deviants:

“PHIG” was the acronym for “Platform for Hosting Infinite Generativity”: rather than build a single game, our goal was to build a platform on which rules modules could be hosted, allowing users to mix and match game-rounds for their friends; infinite generativity is the concept of infinite valid combinations arising from finite components (which I’d first learned about with reference to words and language: for example, it’s possible to write an infinite number of different, “valid” novels1 using the same vocabulary).

As we hashed over how this might take shape, we ultimately took our inspiration from BeZerk’s flash-game, Acrophobia and JackBox-style party games. By demo-day we had two working game modes, managed to convince many members of the audience to bring laptops, and hosted a couple of rounds with about twenty participants: seeing the leaderboard shuffle madly while hearing cries of triumph and dismay pepper the viewers was glorious. It kills me that we don’t have footage.

Notable Features

  • WebSockets for real-time, simultaneous, interactive play using the ws library
  • Two game modules implemented: Synonyms and Rhymes
  • Automated answer-bank generation for nearly limitless replayability
    • Both modules use the random-words library to generate seed-words that are relatively common in English.
    • The Rhymes module uses the DataMuse API to populate a bank of valid rhymes
    • The Synonyms module originally used the cheerio web-scraping library to navigate the DOM of queries. This was an exciting but brittle approach, and in returning to this project in 2023, it was necessary to switch over to the DataMuse API for better sustainability.
  • Persistent Daily Scoreboards for each game type using MongoDB.
  • Amusing, auto-generated (but consistent-by-name) avatars for players originally using the Avatars API (now defunct, alas), now updated to the DiceBear Thumbs API.
  • The Tim Johns Confetti Period follows each round with a magnitude of celebration proportional to how well players scored. This feature was added at the request of a beloved Lighthouse Labs mentor who discovered the react-confetti library and invited us to use it.
  • A dynamic real-time scoreboard using the react-flip-move library that re-arranges itself during frantic game play for Heightened Emotional Impact, etc.


⚠️ Warning: This project was written in 2018 and uses dependencies with versions from that time. Installing those dependencies raises several vulnerability alerts in 2023, but updating them, tracking down breaking changes, and getting everything up and running again is currently beyond the scope of this restoration-for-demonstration-purposes. Therefore, this project is not secure. Beyond enjoying it with friends, please do not attempt to host it perpetually, especially on sensitive or personal hardware.


  • Node.js (here running on 18.12.1, but could probably run on older)
  • MongoDB (here running on 4.2.13, but could probably run on newer)


Note: the structure of the project is a bit weird in that it contains both the server and client components in ./server and ./client respectively. Each of these components has their own package.json and will need to be running during operation. The instructions below will walk you through the process.

  1. Clone the repository:

     $ git clone phig
     $ cd phig
  2. Install dependencies for both the components. It will be necessary to ignore dependency vulnerability warnings.

  phig $ cd server
  phig/server  $ npm install

  # ...

  phig/server  $ cd ../client
  phig/client  $ npm install


  • Optionally: You can set which game modules will play by uncommenting the desired playstyle in server/game/Room.js at the beginning of startGetReady():

    // server/game/Room.js
    // ...
      // #############################
      // #############################
      //  Round Lifecycle Controllers
      // #############################
      // #############################
      async startGetReady() {
        // Normal, random mode:
        // this.round = new this.gameModes[Math.floor(Math.random() * this.gameModes.length)](this.messager);
        // Rhyme-mode only, for n3wbz:
        // this.round = new RoundRhymes(this.messager);
        // Synonym-mode only, for l33tz:
        // this.round = new RoundSynonyms(this.messager);
        // Alternating modes:
        this.round = (this.roundNumber % 2) ? new RoundRhymes(this.messager) : new RoundSynonyms(this.messager);


Note: MongoDB must be running during operation so that high scores can be checked and persisted. If it’s not currently running as a background process, it can be started in a terminal with the command mongod.

To start the game, both the server and the client need to be running at the same time. The easiest way to do this is to run the startup script in client/package.json:

phig/client  $ npm start

In addition to starting up the game, this will open a browser window on the local machine to the client at localhost:3000.

Multiplayer and Hosting

The game isn’t much fun alone. If there are players on your local network, they can join you by navigating to your local IP address at port 3000. (For example,

At present, playing online is possible, but it takes some doing. Basic tunnelling services like localtunnel don’t support websockets, and while ngrok does, a paid account is necessary to host the two tunnels needed to expose the client and the server. The following is the solution I’ve found (with much gratitude to present-day Tim Johns and Brian Estany for helping me get here).

Additional Requirements for Hosting Online

  • ngrok installed (a free account is fine)
  • Three terminal windows (for maximum impressiveness)


  1. Before starting the server or client, in the first of your three terminal windows, run ngrok in Transmission Control Protocol-mode, pointing at PHIG’s server’s port (default 3001):

     $ ngrok tcp 3001
     ngrok                                               (Ctrl+C to quit)
     Visit http://localhost:4040/ to inspect, replay, and modify your req
     Session Status                online
     Account                       Some Account Name (Plan: Free)
     Version                       3.0.5
     Region                        United States (us)
     Latency                       32ms
     Web Interface       
     Forwarding                    tcp:// -> localhost
     Connections                   ttl     opn     rt1     rt5     p50
                                   0       0       0.00    0.00    0.00

    This will make a websocket-compatible tunnel available for the server, once it starts.

  2. Copy the forwarding address without its protocol prefix shown in the resulting ngrok status readout. In the example above, this would be

    NOTE: The address will change every time you restart ngrok unless you have a paid account.

  3. In client/src/config/hosting-config.json, set the remote_address key to that forwarding address:

       "remote_address": ""
  4. In the second of your three terminal windows, start the PHIG application:

     phig/client  $ npm start
  5. In the third of your three terminal windows, use localtunnel to expose the PHIG client with either the npx application runner or a local installation, as desired:

    NOTE: You will need your local IP address for localtunnel’s --local-host parameter. You can grab it quickly with hostname -I.

    NOTE: localtunnel allows you to request a --subdomain, which will provide a more convenient forwarding address if available. In the example below, phig is used.

     $ hostname -I
     $ npx localtunnel --local-host 192.168.X.Y --port 3000 --subdomain phig
     your url is:
  6. It will be necessary to share the insecure HTTP version of the provided address (, rather than in the example above). Testing has shown that it’s often helpful for those who wish to join to be using Chrome (as Firefox seems to resist connecting to the insecure protocol). With all that out of the way, have fun!

  1. “Novel” being a satisfyingly apropos medium for this example, though “reportedly”, I could’ve “essayed” a different one with similar success. </twee-word-nerdery> ↩︎