The next web is

High-performance sync for multiplayer web apps

Why Reflect?

120 FPS cursor

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.

Servers: Pretty Great

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.

Persistence

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.

Persistence

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.

Offline

Local-First

Set kvStore: 'idb'  and data is also stored on the client, providing instant (“local-first”) startup, navigation, and offline support.

On-Prem

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

Latency: low
0

Console

Server Console

Client 2

Latency: low
0

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

Latency: low

Console

Server Console

Client 2

Latency: low

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.

Hobby
Pro
Startup
Enterprise
Hours Included
1,000
2,000
20,000
Custom
Base Price
Free
$30
$300
Custom
Additional Hours
N/A
$0.015
$0.012
Custom
Source License
More info
Unminified build, useful for debugging
Not included
Not included
Included
Included
Private Discord Channel
Not included
Not included
Not included
Included
Managed On-Prem
Not included
Not included
Not included
Included

Examples

  1. Bob is in room giraffe from 9am to 11am. Sally joins giraffe at 10am and stays until 12:30pm. That's 3.5 room hours (9am – 12:30pm).
  2. 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

“The realtime Next.js conf experience is powered by Reflect … proven today to the tune of 190,000 messages per second 🤯.”
Guillermo Rauch
Guillermo Rauch
CEO
“Reflect saved us years of engineering work on a collaborative diagraming tool. The developer experience feels like saving state locally, but it automagically syncs and resolves conflicts.”
Noam Ackerman
Noam Ackerman
Head of Canvas by Monday

Contact Us

We would be happy to answer more questions. You can contact us by email, on Twitter, or on Discord.