At Canvas we're building the frontend for the modern data stack. Fittingly, we’ve ended up implementing this on what we call the “modern web stack” - though some of these technologies are so bleeding-edge this appellation could be premature. The choice of stack might be the most consequential decision an early technical founder makes. Using an emerging technology is fraught with risk; there's relatively sparse documentation, unknown limitations, and little prior art. Yet one year in I remain confident we picked the right tech.
For our inaugural engineering post I’ll explain how we ended up on our frontend stack at the end of the world.
Our mission is to bring the fruits of the data revolution to the business users that have until now been locked out. From the beginning we saw this as a design problem first and foremost. Any analysis is possible with SQL. Distilling this expressiveness to a familiar, usable UI would be the challenge.
We decided spreadsheets were the key. This interface is not only universally familiar, but also maps elegantly to SQL operations. Filters become
where clauses; pivots become
group by statements; a sort becomes an
order by. Data teams and business teams had been speaking the same language all along without even knowing it.
Looking over the landscape of existing spreadsheet tools and their shortcomings for data analysis we arrived at other principles for our tool.
Our spreadsheets should work with billions of rows - scrolling, filtering, sorting, and writing formulas. And this should be fast. In our book there's no difference between slow and broken.
We also decided the traditional one-sheet-per-view model was incorrect. The models in an analysis form a dependency graph. A pivot table aggregates over another table, which itself might be the result of joining two other tables. Data analysis almost always involves multiple related tables like this. Our UI needed to be expressive enough to reflect that.
Relatedly, our users wanted to be able to create pixel-perfect dashboards - something that's mostly impossible in existing tools. The state of the art is copying and pasting charts into a PowerPoint presentation.
Finally, we wanted users to be able to collaborate inside the app in real-time. Users should be able to work on the same table without worrying about conflicts. This called for an architecture where the client and server would run identical code, with the server being the final arbiter of truth - a familiar paradigm from multiplayer video games.
Weighing the options
Wasm isn't written directly, but rather you compile your code into Wasm from a higher-level language. Here we had to choose between C++ and Rust as both languages can compile into WebAssembly. Our multiplayer requirement came into play here. Since we wanted our web server running the same core code as the client we chose Rust for its superior web framework ecosystem. Rust’s excellent type safety was an added benefit.
Our performance and design goals led us to use WebGL for our core UI instead of the DOM. We wanted to load and display data fast and then let users scroll through tables and drag elements at 60fps. Updates through the DOM would not meet these responsiveness goals. The further requirements that users be able to create pixel-perfect reports, and then be able to view the graph of tables backing those charts, meant that the DOM would be too restrictive.
Putting it together
Our current frontend stack is:
- WebGL for rendering the core canvas UI
- Rust compiled into WebAssembly for multi-player state management and performance-bound application code
- React to handle the boiler-plate application code
On the backend we have a Rust web server running the same core code as the browser clients. The clients communicate with the server - and by proxy with each other - via WebSockets, with the server acting as the arbiter of truth in deciding what messages to publish.
One year in we’re happy with our choices. Rust is simply a pleasure to work with. WebAssembly and WebGL are blazing fast and we’ve had surprisingly few browser compatibility issues.
There are times when we feel plainly that we’re on the bleeding edge of the web stack. Writing components in WebAssembly and WebGL is significantly slower than using existing React components, so I would caution against using these frameworks unless you absolutely need the performance benefits. There’s also a dearth of practical documentation or articles about deploying this stack. And you'll find yourself re-implementing much of the functionality you took for granted in the DOM, such as copy and pasting and even scrolling.
For the most part these are fun challenges. It's thrilling to be at the cutting edge of a technology that we hope and believe will become massively important. At Canvas we’re looking forward to building out this ecosystem by building component libraries, discovering new ways to interoperate with React, and publishing articles about our stack. If this sounds exciting to you, we’re always looking for new adventurers to join our expedition.