Run Audience Polls, Presentations, and Contests on Glitch
At Chrome Dev Summit we like to do something interactive with the audience between talks, basically as a kind-of content palette cleanser, and 2019 was no exception.Throughout the course of the day, we presented 16 web features that appeared in Chrome that year, and got the audience to vote for their favourite in a knockout-style contest. We called it "Big Web Quiz", which, as a name, doesn't entirely make sense, but we already had the domain name, and we're lazy. At Chrome Dev Summit we like to do something interactive with the audience between talks, basically as a kind-of content palette cleanser, and 2019 was no exception. Throughout the course of the day, we presented 16 web features that appeared in Chrome that year, and got the audience to vote for their favourite in a knockout-style contest. We called it "Big Web Quiz", which, as a name, doesn't entirely make sense, but we already had the domain name, and we're lazy.
Some folks asked us for the source so they can run their own votes and contests. We thought about releasing it on GitHub, but pfft, who can be bothered setting up their own server just to try a silly thing like this? So instead, we put it on Glitch so it can be easily remixed, meaning you can have your own instance running in no time.
Important note: We threw this project together really quickly, and given I've been ranting about code readability lately, there's code in there that will make me look like a big stinky hypocrite. Don't @ me. I mean, look at this.
During the initial review of this article, folks pointed out that it was a bit text heavy. They're totally right. However, I couldn't think of a meaningful way to break it up, so I've just shoved in a few photos I've taken recently. Enjoy!
Getting it running #
Take a look at the readme for instructions on getting your own instance running, and a tour of all the features.
Anyway, for the rest of this article I want to dive into some of the things I learned while building the app. If all you want is to play with the app and set up your own instance, you've really gotten all you need from this article. We should part company now, as friends.
Everyone else...let's go.
Persisting data on Glitch #
Glitch likes to nap, which is totally relatable. Specifically, Glitch projects sleep after 5 minutes of inactivity, and boot up again next time someone visits. This is what makes Glitch work at scale, and there are a few things you can do to reduce the impact of this.
Your application can create a .data directory in your project root. This directory is special to Glitch: it's persisted, isn't accessible to others, and isn't copied when folks remix your project.
You can access .data via your app's console (Tools > full page console) if you need to snoop around for debugging.
For Big Web Quiz, I used a single JSON file for storage. This is kinda inefficient, because the whole file needs to be rewritten if anything changes, but I only write a subset of app state that updates infrequently.
All of the data about an active vote is held in memory, which means it'd be lost if there's no activity for 5 minutes. That was totally fine for us, but if you're looking to run votes for multiple days, you'll have to make some changes.
If you're changing persistent data frequently, it's probably best to use a smarter file-based data store, such as Lowdb, NeDB, or SQLite. Luckily, there are example projects for all of these.
For what it's worth, here's where I handle the reading and writing of the JSON file.
Session data #
In server-driven apps, you often want to associate data to a particular visitor, such as log-in state. express-session makes this easy, but by default it stores everything in memory, meaning it all vanishes after 5 minutes of inactivity.
Thankfully, session-file-store solves this, by writing session data to disk, and the best place to write to is the .data directory. Here's where I set that up. If you're using one of the smarter storage systems mentioned above, you'll be able to find a plugin for express-session that will use it.
Handling larger build scripts #
Glitch is best-suited to things that have a tiny build script, or no build script at all, but nahhhh. I want bundling, I want TypeScript, I want CSS nesting, minifying, compression... I want it all!
Again, the .data directory is a great build destination, but there are a few things to bear in mind…
Keep rebuilds to a minimum
Glitch projects probably don't have as much CPU power as your local machine, so try to avoid doing more work than you need to. If any of your build tools have an incremental mode, use it. Just make sure any build metadata goes into .data.
I'm using Rollup to manage the build, but I couldn't find a TypeScript plugin I liked, so I wrote my own. It shells out to the official tsc compiler, meaning I can use newer features like incremental builds.
Avoid unnecessary rebuilds
Glitch will run npm run start whenever project files change. However, Glitch will do exactly the same thing when your project wakes up.
This is a problem. You need to rebuild when files change, but if your project is simply waking up, a rebuild is unnecessary – it just makes your project much slower to start.
To work around this, I wrote a little script that writes the last modified time of relevant project files to a JSON file in .data (have I mentioned how useful .data is?). Then, next time npm run start is called, it only calls the build script if some of the files have changed.
This makes project start-up really fast. Pain in the arse though, isn't it? Boy, it sure would be nice if we didn't have to do this. I'll just leave this here and see what happens.
Developing locally might be faster #
If your project gets big enough, it might be better to develop locally and push to Glitch later. Thankfully Glitch makes this really easy! In your project, go to Tools > Git > Import from GitHub. Give Glitch the name of your repo, and it'll replace your current files with those from your GitHub project's master branch!
If your repo isn't on GitHub, you can push directly to it using your projects git URL, also found in Tools > Git. Unfortunately you can't push directly to master, so you need to:
I love Rollup! #
I used this project to experiment with build patterns, and it turned out a lot better than most of my experiments.
The main part of the build is driven by Rollup and mostly transpiles the server-side TypeScript files to JavaScript so NodeJS can run them. Every page is defined by a JSX component which is rendered to HTML by the server.
This means the server script can 'import' scripts intended for the client. This works by creating another Rollup instance within the main Rollup build, which exclusively handles client-side JavaScript, and returns the hashed URL of the final asset, along URLs of its dependencies.
Here's the plugin I wrote to do that. It started as an experiment, but I'm really happy with it. It feels less 'magic' than other build scripts I've used in the past.
I'm not sure the creators of Rollup intended it to be used like this, but Rollup's simple, extensible, and well-documented plugin system made it really nice to play with.
Other 'interesting' things #
Please have a poke around the code, and if you make changes that you think will be useful to others, please make a PR. Here are a few additional tidbits that may be of interest:
- Websockets just work on Glitch! I used them for time sensitive bits of communication that didn't fall into a request-response pattern. The ws library makes it a lot easier. Here's where we integrated it.
- Glitch's CDN will gzip assets – it just works. However, it won't gzip things you serve yourself. I wrote a little plugin to brotli-compress assets at build-time, and used express-static-gzip to serve them.
- Some of the visuals on the big-screen view synchronise to the music. I used a small Preact component to retain the previous view until the music was ready to change. In terms of synchronising the music itself, here's a guide (please give that a sympathy-click. It's the most effort I put into a blog post, no one really cared).
- I wrote another little component to allow programmatic transitions between components. This was particularly handy for the 'slam' animations, which involve randomisation, and can't easily be expressed with CSS.
And that's it! #
I had a lot of fun throwing this project together. The quick-turnaround means some of the project is a bit rough-and-ready (especially the admin UI, sorry about that), but I hope you have fun playing around with it!