The next web is
High-performance sync for multiplayer web apps
Why Reflect?
Absurdly Smooth Motion
Throw away your interpolation code. Reflect syncs changes at 120 FPS (hardware permitting). Built-in batching and buffering provide buttery smooth, precision playback automatically — across town or across the globe.
Transactional Conflict Resolution
CRDTs converge, but to what?? Validation and custom business logic aren't possible.
Reflect uses a more powerful technique known as Server Reconciliation. Your mutation code runs server-side and is authoritative. Clients are guaranteed to converge with server changes.
Mutators can enforce arbitrary business logic, fine-grained authorization, server-side integrations, and more.
First-Class Text
Reflect offers first-class support for text via the industry standard Yjs text editing library. All popular editors including TipTap, Monaco, and ProseMirror are supported out of the box.
And with server-authority, Yjs gains a superpower: server-side validation that allows you to easily implement length limits, content filtering, and more.
Automatic Persistence
There's no separate, slower persistence API to juggle. Write changes as they happen (yes, every mouse movement) and they are stored continuously and automatically, up to 50MB/room.
Local-First
Set kvStore: 'idb'
and data is also stored on the client, providing instant (“local-first”) startup, navigation, and offline support.
On-“Prem” Available
Use Reflect as a traditional SaaS, or deploy it to your own Cloudflare account.
We'll run, monitor, and update it. You maintain control, ownership, and business continuity with a perpetual source license.
How it Works
Step 1: Create Room
Users connected to the same room see each others' changes in realtime.
1import {Reflect} from "@rocicorp/reflect/client";
2
3const reflect = new Reflect({
4 reflectAPIKey,
5 roomID: "myFirstRoom",
6});
7
8reflect.onConnect(({roomID}) => {
9 console.log(`Connected to room ${roomID}`);
10});
Step 2: Define Mutators
Mutators are how you make changes to rooms. They are JavaScript functions you define, that run on both the client and the server.
By replaying mutators on the server, Reflect naturally resolves many types of conflicts, while allowing for custom, authoritative server logic.
Try it: Use the demo below to increment a multiplayer counter. Increase the latency and quickly increment on both clients. Notice how normal arithmetic logic naturally sums concurrent operations, without the need for CRDTs.
1export default {
2 async increment(tx, args: {key: string, delta: number}) {
3 const {key, delta} = args;
4 const prev = await tx.get(key, 0);
5 const next = prev + delta;
6 console.log(`Running mutation ${tx.clientID}@${tx.mutationID} ` +
7 `on ${tx.location}: ${prev} → ${next}`);
8 await tx.set(key, next);
9 },
10}
Client 1
Console
Server Console
Client 2
Console
Step 3: Render Reactively
Subscribe to changes in Reflect and render your UI reactively. There's no need to interpolate. You receive updates at up to 120 fps, just as if the collaborator was local.
Try it: Notice how even when the latency is high, changes playback on the receiver exactly as they happened on the source.
1import {Reflect} from "@rocicorp/reflect/client";
2import mutators from "./mutators";
3const authToken = "$your-auth-token";
4const roomID = "myFirstRoom";
5
6const r = new Reflect({
7 reflectKey,
8 authToken,
9 roomID,
10 mutators,
11});
12
13r.subscribe(tx => tx.get("degree"), val => {
14 console.log(`Key "degree" changed to: ${val}`);
15 document.querySelector("#degreeImage").rotation = val;
16});
17
18<Slider onChange={(val) => r.mutate.setDegree(val)} \>
Client 1
Console
Server Console
Client 2
Console
You're Done.
Reflect publishes your mutators to a unique sandboxed environment. Rooms are backed by Cloudflare's Durable Object technology and scale horizontally by room.
Shell
>reflect publish
🎉 Published! Running at https://myapp.reflect.net/.
Get Started Now
Build your first multiplayer app in under a minute:
Simple, Usage-Based Pricing
We charge by monthly active hours. A room is active when it has one or more connected users. Background tabs disconnect automatically.
Examples
- Bob is in room
giraffe
from 9am to 11am. Sally joinsgiraffe
at 10am and stays until 12:30pm. That's 3.5 room hours (9am – 12:30pm). - Same as above, but James is concurrently in room
platypus
from 10am to 12pm. That's an additional 2 hours for a total of 5.5.
Early Reactions
Contact Us
We would be happy to answer more questions. You can contact us by email, on Twitter, or on Discord.