Sealion

A browser-based drum synthesizer that supports real-time collaboration via conflict-free replicated data types (CRDTs) and WebSockets


Hero image for Sealion

Sealion is a real-time collaborative drum synthesizer that runs in the browser. The key concerns for this project were audio synthesis on the web, achieving precise timing, and synchronizing distributed state data. The project takes its name from Ronan the sealion who can maintain a beat better than some humans. This project emerged from my exploration of conflict-free replicated data types (CRDTs) and their application to creative multiplayer apps during my time at Recurse Center. The design style is inspired heavily by Teenage Engineering’s synthesizers, which have a minimalistic and playful look.

Although the app is still in development, it relies on a custom operation-based CRDT implementation, which is transmitted and synchronized over WebSockets. Rather than transmitting entire application states, the system sends only the operations needed to update state, reducing the volume of metadata needed to be transmitted over the network. The CRDT enables eventual consistency across multiple clients without conflicts and, in turn, allows users to collaborate in real-time without edit conflicts or state desynchronizations.

Audio processing presented unique challenges around time precision. Because the app is a drum synthesizer, it relies on a clock to trigger the synthesizers. However, JavaScript’s event loop introduces audible millisecond delays, so I initially experimented with Web Workers to create reliable timing mechanisms. After testing and reading Chris Wilson’s helpful article on timing in JavaScript, I learned that the Web Audio API’s sample-accurate scheduling could offer sub-millisecond precision, which I implemented as a custom React hook for a clock. The synthesizer architecture uses Tone.js with custom audio node chains, allowing for dynamic sound generation and real-time parameter manipulation via React state.

This project also required careful attention to memory management because Web Audio contexts can easily create memory leaks if not managed properly. I developed custom React hooks to manage the audio context lifecycle and prevent performance degradation over extended sessions. The UI also needed to handle frequent parameter changes without causing audio dropouts, which required carefully managing React’s rendering process.