A peer-to-peer web application piano teachers and students use to share their keyboard inputs in near real time.

Midishare is a TypeScript monorepo of my own design, the key components being:

  1. Client: typescript, react, tailwindcss, and react-query
  2. Server: typescript, node.js, express, sqlite
  3. Keyboard: Internal NPM package wrapping the keyboard component used by the Client SPA. typescript, react, three.js
  4. MIDI inspector: Internal web application for inspecting live midi messages from controllers. typscript, webmidi, react, tailwindcss

Docker Compose powers the highly portable, high-production-parity development environment, which runs all of the above.

Midishare is deployed to Digital Ocean, on a Droplet running Dokku. Deployments to Dokku remotes (client, server) are configured to build from the same respective Dockerfile used in development (albeit with a different Dockerfile build target).

Static assets are served from an nginx process (separate from the Dokku nginx process) on the host, and then accelerated by BunnyCDN. Not quite Jamstack, but it's close and retains nginx access logs.

Monitoring is an open question, but I aim to use Grafana Cloud free tier as both a learning opportunity and an affordable/flexible/complete solution.


My personal website. Build with gulp, postcss, and posthtml using TailwindCSS for styling.

I decided to ditch Hugo after not being able to remember its esoteric conventions... for my own esoteric conventions comprised of posthtml plugins (posthtml-include, posthtml-expressions, and posthtml-content).

The website is hosted on Netlify, deployed on push to the GitHub repository. I manage DNS in Cloudflare to keep it simple and decoupled from the provider. That means I opt out of the Netlify CDN, but Cloudflare will pick up that slack for free.