As a proof of concept I want to build a multiplayer game. The application consists of two pages:
The result is on GitHub. The game is written in nbb and using the deno runtime. The UI is powered by datastar. There is a little bit of styling done with bulma. The runnable code is a single file with no configuration files necessary (no project.json, no deps.edn). No build is required. Everything is in server.cljs. There are some other files in the repo, but they are for convenience either with Emacs or with nbb.
Both players agree on an id (a string). Both open the browser and enter the string into the text field.


Then, they can play tic-tac-toe against each other.

If someone tries to join when 2 players are already playing they get a notification:

The winner is marked by underscore.

If you are using Emacs, open the server.cljs and then M-x cider-jack-in-cljs. Because of the .dir-locals.el this should just work if you have deno installed. Eval the file with Ctrl-c,Ctrl-k (or any other method of your choice).
In the repl, change the namespace to server with (ns server) and then run (start-server). This should give you a server running on port 8000.
You can inspect the open connections by evaluating the atom @all-streams. If you change the routing or just want a fresh start, run (restart) in the repl. Especially when the routes function is changed, you need to restart.
All updates to the UI are pushed via SSE (server-side events). In Chrome, you can see these events if you look at the connect request in the EventStream tab.

There is also a docker file in the repo, mainly to demonstrate how easy it is to create one with the approach above (copy the file and run deno)
To start the server, run deno run server or the command it refers to in the deno.json file.
There are several different ways to split up things with datastar. I started off with a lit component and the full board as a signal. However, I wanted to drive the UI more out of the backend, so currently I'm pushing the whole board as a fragment, so the frontend does not have an abstract notion of the board (or a 'model', if you like). I also had a version just updating a single element of the board, but that proved to be cumbersome when there was a winner etc. I'm sure there are even more ways to do it with signals. My initial version which was somewhat similar to an MVC pattern didn't work well, anyway. The session handling is very basic. I've run into several cases where I needed to restart the server.
It would be nice to see how many connections a single server can handle. I'm thinking of running playwright workloads with artillery to find that out. Some playwright tests might be nice in general.
Trying to get this running on deno deploy would also be nice.
And lastly, to make this solution scalable, I would like to see if the board can be stored in a deno KV store, and have several instances running with a reverse proxy (the game_id would make a good candidate for the session affinity).