Building a Real-Time Collaborative Whiteboard with Svelte and WebSockets
When thinking about modern collaborative tools, names like Miro or FigJam immediately come to mind. Behind their apparent fluidity lie formidable technical challenges: state synchronization, conflict management, and near-zero latency. But do we have to reinvent the wheel with exotic protocols or real-time databases? Not necessarily. With Svelte and well-mastered WebSockets, it is possible to build a performant collaborative whiteboard without sacrificing simplicity.
This article guides you step by step through building such an application, drawing on experience from similar projects and community feedback. We will cover architecture, synchronization, user management, and optimizations that make a difference.
The Foundation: Svelte and WebSockets, a Winning Duo
Svelte stands out for its native reactivity: reactive variables update the DOM without overhead. Coupled with WebSockets, which enable persistent bidirectional communication, we get an ideal foundation for real-time.
In our case, each drawing action (stroke, erasure, element movement) is sent via WebSocket to a Node.js server, which broadcasts it to other clients. The global state is maintained on the server side, but each client has a local copy to ensure instant response.
Architecture: The Server as Conductor
The server plays a central role: it receives events, validates them, and redistributes them. To avoid conflicts, each event is timestamped and identified by a unique ID. In case of concurrency on the same shape, the last received event takes precedence – a simple but effective "last-write-wins" approach for a whiteboard.
Session management is essential: each whiteboard has a unique identifier. Clients connect to a specific room, and the server only transmits events to members of that room. This limits load and ensures privacy.
State Management: The Right Balance Between Local and Remote
A classic pitfall is to synchronize everything in real-time, including transient states like hovering over a shape. It is better to distinguish:
- Permanent events: strokes, movements, property changes – sent to the server and persisted.
- Transient events: other users' cursors, temporary selections – broadcast locally via WebSocket but not persisted.
This distinction reduces server load and avoids cluttering the database with unnecessary information.
User Experience: The Cursor of Others, a Window into Collaboration
Seeing a collaborator's cursor in real-time creates a sense of presence. Position data is sent at high frequency (every 50 ms) but without delivery guarantee. To avoid saturation, client-side throttling is used.
Note: scrolling or zooming the whiteboard must be taken into account. Each mouse event is converted to logical coordinates (relative to the canvas), not screen pixels, so that all users see the cursor in the correct location.
Conflict Management: Undo/Redo in Multiplayer Mode
Undo/redo is a headache in a collaborative environment. The chosen solution: each user has their own local undo stack, but undo actions are converted into "inverse" events that are sent to the server. Thus, undoing a stroke amounts to sending a "delete this stroke" event to everyone. This approach, though more complex, avoids inconsistencies.
For further reading, Liveblocks offers an excellent analysis of undo/redo strategies in multiplayer environments, which inspired our implementation.
Performance and Scalability
For moderate usage (fewer than 100 simultaneous users on the same whiteboard), a single Node.js server suffices. Beyond that, a distributed architecture with Redis for state sharing between instances should be considered. NATS, mentioned in similar projects, can also serve as a messaging backbone.
On the client side, HTML5 Canvas is preferred over SVG for rendering performance. Shapes are redrawn each frame, but only modified areas are updated using dirty rectangles.
Deployment and Practical Considerations
The WebSocket server can be deployed on a platform like Heroku or Fly.io. Beware of long connections: load balancer timeouts must be configured not to cut WebSockets. SSL is essential for production environments.
For storage, a simple database like SQLite or PostgreSQL can record the final state of the whiteboard (list of shapes with their properties). For more advanced real-time needs, Supabase Realtime offers a turnkey solution.
Lessons Learned: Experience Feedback
During the construction of a prototype, several pitfalls were identified:
- Do not neglect compression: WebSocket messages can be compressed with permessage-deflate to reduce bandwidth.
- Handle reconnection: after a connection loss, the client must request the full whiteboard state to resynchronize.
- Test with real users: unit tests cannot replace real sessions with dozens of people drawing simultaneously.
Conclusion
Building a collaborative whiteboard with Svelte and WebSockets is both an accessible and educational project. By following a simple yet robust architecture, properly managing states and conflicts, you get a smooth and enjoyable application. Modern tools like Svelte simplify reactivity, while WebSockets ensure real-time communication.
So, ready to get started? Open your editor, install a few dependencies, and start with a simple exchange of mouse coordinates. The rest will come through iteration.
Further Reading
- Medium - Building a Real-Time Collaborative Whiteboard Backend with NestJS and Socket.IO - A detailed article on a similar approach with NestJS.
- Supabase Realtime Docs - Documentation on Supabase's real-time capabilities, useful for storage and synchronization.
- Reddit - How to start learning WebSockets - Discussion with practical tips for beginners.
- Reddit - Reactive, optimistic-by-default WebSocket library - Presentation of a reactive WebSocket library for collaborative apps.
- Hacker News - Matrix-CRDT: real-time collaborative apps using Matrix - An alternative approach based on Matrix and CRDTs.
- Hacker News - How to build undo/redo in a multiplayer environment by Liveblocks - Analysis of undo/redo strategies in multiplayer environments.
- Reddit - itty-sockets: dead-simple realtime messaging in Svelte - A lightweight library for real-time with Svelte.
