Our pixel-rendered planner with a Rust backend
Background
Tweede golf is by no means a mega-corporation (there's 18 of us), but keeping track of who works on which project, what they are currently doing and whether they're available to help out on an awesome project next week... is a bit of a hassle on paper.
In 2015, we were old-school and used a whiteboard. No lie. Here's a picture.
But today (and for a while now), we have a pretty neat online version, home-made, with TypeScript and Rust of course!
The Tech
Where it came from
Once it was decided that the whiteboard had to go, we started developing, mostly in React, with a NodeJS backend, and have since had several versions of this planner called TGenda.
As the company grew, and the number of actual plans to be recorded grew with it, so did the number of DOM elements. This resulted in performance issues for the React/HTML build we were using, because we used the drag-and-drop functionality a lot. This was problematic: we needed that to be smooth, so it was time for a reimplementation.
Wanting to get rid of the DOM elements brought me to the Canvas API, which uses pixels instead, because who doesn't love awesomely fast animations? (More about that later.)
We had previously experienced that maintaining a TypeScript frontend with many dependencies is tricky and tends to bring about security issues. So for the greater good I deciced to challenge myself to not use any runtime dependencies for the frontend at all, except of course for TypeScript itself, as a direct dependency.
The backend was created using Rust with the excellent Axum and SQLX crates.
The current version in Rust
This version has an exceptionally clean and organized codebase:
- A global state, that is updated through events and a reducer (inspired by redux)
- State updates trigger a render of the whole canvas
- Renders are quick - mere milliseconds
- The Rust backend has the same event type as the frontend, and a bus that several services can listen to
- A selection of frontend events is also sent to the backend through a websocket connection
- The backend broadcasts these events to all other clients, creating a live update mechanism
- The backend runs a service that listens to certain events and turns these into database updates
How it's made: our digital company planner
This event-based mechanism is very flexible; by changing only one line, I was able to make the scroll position a shared state, which means every client then shared a scroll position (for fun, just to test the performance). The combination of a websocket and the Rust backend provides such low latencies that multi-client scrolling is smooth!
The frontend looks similar to this example - try dragging an event to a different day!
Other things I'm happy about:
- Forms are rendered with HTML, by my own implementation of JSX, without Virtual DOM like in React. This makes the code readable and allows you to easily follow the logic behind it. It delivers excellent performance for our use-cases.
- The application is only a single Rust binary! All static files (like JavaScript and CSS) are put into this binary at compilation time and served straight from memory.
The best features
And the features that get their own paragraph are confetti 🎉 and the snake 🐍︎ game.
Make confetti to celebrate never having to be bored:
And then play snake to literally eat your work: