When I started building Modeldraw, a collaborative diagramming platform, I knew real-time collaboration would be essential. Multiple users needed to edit the same diagram simultaneously without conflicts, see each other’s cursors moving in real-time, and have everything work seamlessly whether they were logged in or accessing via a shared link.
What seemed straightforward at first turned into one of the most complex technical challenges of the entire project. Here’s how I solved it.
The Requirements
Real-time collaboration needed to handle:
- Concurrent change merging — When two users edit the same element simultaneously
- Live cursor tracking — Showing where other users are working with smooth animations
- Flexible authentication — Supporting both authenticated users and anonymous viewers with share links
- Reliable synchronization — Ensuring diagram state stays consistent across all clients
SignalR vs. WebSockets: Choosing the Right Foundation
The first major decision was choosing between raw WebSockets and SignalR. While both enable real-time communication, SignalR provides crucial abstractions that saved weeks of development time:
Transport Flexibility
WebSockets only work when both server and client support them, and network intermediaries like proxies or firewalls can block the connection. SignalR automatically selects the best available transport method and falls back gracefully. Your application works reliably across different browsers, devices, and network conditions without manual fallback logic.
Developer Experience
With raw WebSockets, you manage connections, message formats, serialization, and reconnection logic yourself. SignalR’s hub model lets clients call server methods and vice versa, almost like regular method calls. This simplified mental model significantly reduced complexity.
Built-in Resilience
When WebSocket connections drop, you must detect failures and implement reconnection logic. SignalR handles automatic reconnection with customizable retry strategies out of the box.
Scalability Features
WebSockets require custom implementations for tracking clients and broadcasting messages. SignalR provides groups and broadcasting natively, making it trivial to send updates to all users viewing a specific diagram.
ASP.NET Core Integration
SignalR integrates seamlessly with ASP.NET Core’s authentication, authorization, and dependency injection systems. On the frontend, the @microsoft/signalr
package provides a TypeScript-friendly API.
The decision was clear: SignalR would provide the foundation we needed.
Solving the Concurrent Edit Problem
The trickiest challenge was merging concurrent changes. When User A and User B both modify the same diagram simultaneously, the system needs intelligent conflict resolution at both the shape level and property level.
Here’s the merge logic I implemented:
Property Addition
If User A adds property P1 and User B’s save doesn’t include it, keep A’s addition. New properties from the first user are preserved.
Property Modification
If both users modify property P2, compare B’s value with the original. If B didn’t actually change it, keep A’s modification. This prevents one user from accidentally overwriting another’s intentional changes.
Property Deletion
If User A removes property P3 and User B tries to save with it, the deletion wins. This ensures cleanup operations aren’t reversed.
This ruleset prevents conflicts while respecting user intent — crucial for maintaining trust in collaborative editing.
Broadcasting Changes to Connected Clients
When a diagram saves, all connected users need immediate updates. I used SignalR groups, where each diagram creates a group identified by its ID:
private async Task DispatchAsync(Element diagramElement, string eventName, object data)
{
await _hubContext.Clients
.Group(diagramElement.Id.ToString())
.SendAsync(eventName, data);
}
This broadcasts to everyone viewing that diagram — both authenticated users and anonymous viewers using share links. The group abstraction makes this remarkably simple.
Applying Updates on the Frontend
When the frontend receives update events, it must merge incoming changes with the current state without disrupting the user’s work. The process:
- Deserialize the incoming diagram state
- Identify which shapes changed
- Reuse existing shape instances where possible (preserves UI state)
- Apply property updates using the same merge rules
- Trigger re-rendering only for affected elements
This selective update approach maintains smooth interaction even during active collaboration sessions.
Lessons Learned
Building real-time collaboration taught me several valuable lessons:
Start with the right abstractions
Choosing SignalR over raw WebSockets saved weeks of development time and provided battle-tested solutions for complex problems like reconnection and transport fallbacks.
Conflict resolution requires clear rules
Without explicit merge logic, concurrent edits quickly become chaotic. Define your rules early and test them thoroughly.
User experience matters more than technical elegance
The system needed to “just work” for users, even if that meant additional complexity in the implementation.
Conclusion
Real-time collaboration in Modeldraw now supports teams creating UML diagrams, flowcharts, and agile workflows together seamlessly. Users can see each other’s cursors, make simultaneous edits, and trust that their changes won’t be lost — all thanks to SignalR’s robust foundation and careful conflict resolution logic.
You can see it in action at modeldraw.com.
Top comments (0)