-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
1708 lines (1650 loc) · 83.7 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Symphony</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
<link href="./stylesheets/styles.css" rel="stylesheet">
</head>
<body class="text-gray-600">
<!-- Nav bar -->
<header class="fixed top-0 left-0 w-full bg-white shadow-lg flex">
<a href="/" class='ml-10 mr-auto'> <img class="symphony-logo"
src="assets/images/logos/transparent-logo-2.png" /></a>
<ul class="py-2 flex flex-row border-b h-full items-center">
<li class="navbar-item">
<a href="#section-1">Case Study</a>
</li>
<li class='navbar-item'>
<a href="#presentation">Tech Talk</a>
</li>
<li class='navbar-item'>
<a href="#our-team">Our Team</a>
</li>
<li class='navbar-item ml-4'>
<a target="_blank" href="https://github.com/symphony-framework">
<img src="assets/images/icons/github-mark.png" class=" w-8 h-auto" />
</a>
</li>
</ul>
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" id="hamburger" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
<ul id="hamburger-menu" class="shadow-lg hidden">
<li>
<a href="#section-1">Case Study</a>
</li>
<li>
<a href="#presentation">Tech Talk</a>
</li>
<li>
<a href="#our-team">Our Team</a>
</li>
<li><a target="_blank" href="https://github.com/symphony-framework">GitHub</a></li>
</ul>
</header>
<div id="intro">
<!-- First intro panel -->
<div class="intro-panels">
<div class="intro-panel fixed-logo">
</div>
<div class="intro-panel bg-symphony-background-blue text-gray-50">
<p class="text-center w-1 text-5xl leading-relaxed font-bold">
<span class="blue">Symphony</span> makes it easy for developers to build
<br><span class="light-green">real-time</span>
<span class="light-blue">collaborative</span> <span class="green">applications</span>
</p>
</div>
</div>
<!-- Second intro panel -->
<div class="intro-panels">
<div class="intro-panel bg-symphony-dark-green text-gray-50 fixed-logo">
<p class="text-center w-1 text-5xl leading-relaxed font-bold">
Simple to Manage and Deploy
</p>
<div class="diagram">
<video class="cli-demo" width="600" height="400" autoplay loop muted playsinline>
<source src="assets/videos/cli-compose.mp4" type="video/mp4">
</video>
</div>
</div>
</div>
<!-- Third intro panel -->
<div class="intro-panels">
<div class="intro-panel bg-symphony-light-blue fixed-logo">
<p class="text-center w-1 text-5xl leading-relaxed font-bold">
Cloud Native and Ready to Scale
</p>
<div class="diagram">
<img src="assets/images/diagrams/scaled.png" class="w-full" />
</div>
</div>
</div>
</div>
<!-- Sidebar -->
<ul class="pl-12 pt-6 border-r fixed w-96 top-24 -left-96 transition-all" id="sidebar">
<li class="sidebar-section" id="section-1-sidebar" data-section="1">
<a href="#section-1">Introduction</a>
<ul class="sidebar-subsection-list" data-section="1">
<li class="sidebar-subsection" data-subsection="1.1">
<a href="#subsection-1.1">Introducing Symphony</a>
</li>
</ul>
</li>
<li class="sidebar-section" id="section-2-sidebar" data-section="2">
<a href="#section-2">Real-Time</a>
<ul class="sidebar-subsection-list" data-section="2">
<li class="sidebar-subsection" data-subsection="2.1">
<a href="#subsection-2.1">WebRTC</a>
</li>
<li class="sidebar-subsection" data-subsection="2.2">
<a href="#subsection-2.2">WebSocket</a>
</li>
<li class="sidebar-subsection" data-subsection="2.3">
<a href="#subsection-2.3">Real-Time Infrastructure in Symphony</a>
</li>
</ul>
</li>
<li class="sidebar-section" id="section-3-sidebar" data-section="3">
<a href="#section-3">Collaboration</a>
<ul class="sidebar-subsection-list" data-section="3">
<li class="sidebar-subsection" data-subsection="3.1">
<a href="#subsection-3.1">Rooms</a>
</li>
<li class="sidebar-subsection" data-subsection="3.2">
<a href="#subsection-3.2">Presence</a>
</li>
<li class="sidebar-subsection" data-subsection="3.3">
<a href="#subsection-3.3">Undo/Redo</a>
</li>
<li class="sidebar-subsection" data-subsection="3.4">
<a href="#subsection-3.4">Conflict and Resolution</a>
</li>
</ul>
</li>
<li class="sidebar-section" id="section-4-sidebar" data-section="4">
<a href="#section-4">Solutions</a>
<ul class="sidebar-subsection-list" data-section="4">
<li class="sidebar-subsection" data-subsection="4.1">
<a href="#subsection-4.1">Do-It-Yourself (DIY)</a>
</li>
<li class="sidebar-subsection" data-subsection="4.2">
<a href="#subsection-4.2">Real-Time Collaboration as a Service</a>
</li>
<li class="sidebar-subsection" data-subsection="4.3">
<a href="#subsection-4.3">Where Does Symphony Fit In?</a>
</li>
</ul>
</li>
<li class="sidebar-section" id="section-5-sidebar" data-section="5">
<a href="#section-5">Building Symphony</a>
<ul class="sidebar-subsection-list" data-section="5">
<li class="sidebar-subsection" data-subsection="5.1">
<a href="#subsection-5.1">Design Philosophy</a>
</li>
<li class="sidebar-subsection" data-subsection="5.2">
<a href="#subsection-5.2">Prototyping</a>
</li>
<li class="sidebar-subsection" data-subsection="5.3">
<a href="#subsection-5.3">Load Testing the Prototype</a>
</li>
<li class="sidebar-subsection" data-subsection="5.4">
<a href="#subsection-5.4">Scaling</a>
</li>
<li class="sidebar-subsection" data-subsection="5.5">
<a href="#subsection-5.5">Load Testing the Final Architecture</a>
</li>
</ul>
</li>
<li class="sidebar-section" id="section-6-sidebar" data-section="6">
<a href="#section-6">The Future of Symphony</a>
</li>
</ul>
<!-- Case Study -->
<div class='ml-96 pl-20 mr-64' id="casestudy">
<!-- Section 1 -->
<div id="section-1" class="section">
<h2 class='section-heading' data-section="1">Case Study</h2>
<blockquote class='blockquote'>
"Alone we can do so little; together we can do so much." - Hellen Keller
</blockquote>
<p class='casestudy-text'>
Collaboration has been a key feature of the internet since its earliest days. The history of collaboration on
the
internet can be traced back to the 1960s, when Douglas Engelbart, the inventor of the computer mouse, <a
href="https://archive.org/details/nasa_techdoc_19660020914/page/n1/mode/2up">studied</a>
how
computers could support collaborative work and communication. He developed a system called <a
href="https://dougengelbart.org/content/view/183/">
NLS (oN-Line
System)
</a>
that allowed users to create and edit documents, link them together, and share them with others.
</p>
<p class='casestudy-text'>
Of course, for much of the internet’s history, collaboration had to be done by taking turns – collaborators
might
email a
document back and forth after each round of changes, for example.
</p>
<p class='casestudy-text'>
Even today, many online editing tools do not allow for concurrent changes by multiple users in real-time.
<a href="https://basecamp.com/">Basecamp</a>, for
example, is an online collaboration platform that allows users to share and work on the same documents.
What
happens, though, when more than one user wants to edit a document at the same time?
</p>
<div class="diagram">
<img src="assets/images/diagrams/basecamp-lockedout.png" class="w-full" />
</div>
<p class="casestudy-text">
As the above image shows, online collaboration via Basecamp is not real-time–only one user is allowed to modify
content
at a time, and others must wait for that user to finish before making their own changes.
</p>
<div class="diagram">
<!-- <img src="assets/images/diagrams/collab-apps.png" class="w-1/2 h-auto" /> -->
<img src="assets/images/diagrams/collab-apps.png" />
</div>
<p class="casestudy-text">
Contrast that with applications such as Figma, Google Docs, Miro, and Trello that allow users to modify shared
content
at the same time - in other words, collaborate in <strong>real-time</strong>.
</p>
<p class="casestudy-text">
<strong>Real-time collaboration</strong> refers to the ability for multiple users to work on the same document
or project simultaneously and see the changes made
by others as they happen. Real-time collaboration has become so prevalent that <a
href="https://www.figma.com/blog/how-figmas-multiplayer-technology-works/">some</a> deem it an essential
aspect
of many
modern web applications, one that has opened up new ways of working together on the web.
</p>
<blockquote class="blockquote">"[Real-time collaboration] eliminates the need to export, sync, or email copies
of
files and allows more people to take part in the design process." - Evan Wallace, Figma
</blockquote>
<div id="subsection-1.1" class="subsection" class="subsection">
<h3 class="subsection-heading" data-subsection="1.1">Introducing Symphony</h3>
<p class='casestudy-text'>
<strong>Symphony</strong> is a framework for providing real-time collaboration functionality to web
applications.
</p>
<p class='casestudy-text'>
What does that mean?
</p>
<p class='casestudy-text'>
Let’s illustrate this with a simple whiteboard application that we’ve created for demo purposes. Users can
perform the
basic actions you’d expect in this kind of app, like drawing lines, adding shapes, changing colors, and so on.
At this
point, this is just a single-user application that does not support multi-user collaboration.
</p>
<p class='casestudy-text'>
Go ahead, give it a try.
</p>
<div id="singleplayer-demo">
<iframe id="singleplayer-demo-iframe" width="100%" height="600" frameborder="0"></iframe>
</div>
<p class='casestudy-text'>
Now let’s add Symphony into the mix. By deploying the Symphony backend to AWS and connecting it with our
whiteboard, the
application is now collaborative. Users can work together in the same collaborative space and see what others
are doing
in real-time.
</p>
<p class='casestudy-text'>
Try and see what happens when you draw on either of the below whiteboards.
</p>
<div id="multiplayer-demo">
<iframe id="multiplayer-demo-iframe-1" width="45%" height="600" frameborder="5"></iframe>
<iframe id="multiplayer-demo-iframe-2" width="45%" height="600" frameborder="5"></iframe>
</div>
<p class='casestudy-text'>
In this case study, we will explore the components of web-based real-time collaboration, review existing
solutions that
can enable real-time collaboration in applications, and present our project, Symphony: how developers can use
it, how we
created it, and the trade-offs we weighed during the development process.
</p>
<p class="casestudy-text">First, let’s explore what we mean by “real-time collaboration.”</p>
</div>
</div>
<!-- Section 2 -->
<div id="section-2" class="section">
<h2 class='section-heading' data-section="2">Real-Time</h2>
<p class='casestudy-text'>
For an application to feature real-time collaboration, the first requirement is that data must be exchanged
between
users, or between users and a server, in real-time. Generally, that means that a user sees updated data either
instantly
or without noticeable delay, and without needing to take any specific action or make any particular request to
see the
updated data.
</p>
<div class="diagram">
<video autoplay loop muted playsinline>
<source src="assets/videos/real-time-example.mp4" type="video/mp4" />
Your browser does not support the HTML5 Video element.
</video>
</div>
<p class='casestudy-text'>
For the purposes of real-time collaboration, data must flow in two directions: from the user, when that user
makes a
change that will be shared with others, and to the user, when someone else makes a change.
</p>
<div class="diagram">
<video autoplay loop muted playsinline>
<source src="assets/videos/broadcast.mp4" type="video/mp4" />
Your browser does not support the HTML5 Video element.
</video>
</div>
<p class='casestudy-text'>
When building a real-time web application, challenges arise because HTTP, the protocol by which most web
communication
occurs, is not real-time by design. Instead, HTTP uses a request-response model whereby the client sends a
request to a
server and then waits for the server to respond with the requested data.
</p>
<div class="diagram">
<img src="assets/images/diagrams/http-request-response.png" />
</div>
<p class="casestudy-text">
So how can we make an application that lets users see a constant stream of updated data? For bi-directional,
real-time
data exchange over the web, two protocols in particular merit our consideration:
<strong>WebRTC</strong> and <strong>WebSocket</strong>.
</p>
<div id="subsection-2.1" class="subsection">
<h3 class="subsection-heading" data-subsection="2.1">WebRTC</h3>
<p class="casestudy-text">
<strong>WebRTC</strong>, which stands for Web Real-Time Communication, is an open-source project that allows
web browsers and mobile applications
to engage in peer-to-peer real-time communication via APIs. The connection between peers is established with
the use of
a signaling server that acts as an initial intermediary, allowing two clients to find each other and negotiate
connection parameters. Once the connection is established, data is transferred directly between the peers via
media
streams and/or data channels.
</p>
<div class="diagram">
<video autoplay loop muted playsinline>
<source src="assets/videos/webrtc.mp4" type="video/mp4" />
Your browser does not support the HTML5 Video element.
</video>
</div>
<p class="casestudy-text">
Since WebRTC is primarily used over UDP, it offers superior latency at the cost of some packet loss. This
makes it an
especially attractive choice for video and audio streaming.
</p>
</div>
<div id="subsection-2.2" class="subsection">
<h3 class="subsection-heading" data-subsection="2.2">WebSocket</h3>
<p class="casestudy-text">
<strong>WebSocket</strong> is a protocol that provides a two-way channel of real-time communication between a
client and a server. The connection
is established via the WebSocket handshake:
</p>
<ul class="casestudy-list">
<li class="casestudy-list-item">
The client sends an HTTP request to the server with an <code>Upgrade</code> header
</li>
<li class="casestudy-list-item">
The server responds with <code>Connection: Upgrade</code> and <code>Upgrade: Websocket</code> headers
</li>
</ul>
<p class="casestudy-text">
Once the handshake is complete, the WebSocket connection is established using the same underlying TCP/IP
connection used
in the handshake. Either party is now free to send data at any time via this connection.
</p>
<div class="diagram">
<video autoplay loop muted playsinline class="w-full">
<source src="assets/videos/websocket.mp4" type="video/mp4" />
Your browser does not support the HTML5 Video element.
</video>
</div>
<p class="casestudy-text">
WebSocket provides better data integrity than WebRTC due to the underlying <a
href="https://ably.com/topic/webrtc-vs-websocket#:~:text=While%20WebSocket%20works%20only%20over,the%20underlying%20reliability%20of%20TCP">reliability
of TCP</a>, which provides
error
detection, acknowledgement, and flow/congestion control mechanisms to ensure the accurate, complete, and
in-order
delivery of data. WebSocket is therefore often used in situations where delivering accurate data is
paramount
(e.g. real-time dashboards, stock price tickers) and where minimizing latency is less critical (e.g. live text
chat).
</p>
</div>
<div id="subsection-2.3" class="subsection">
<h3 class="subsection-heading" data-subsection="2.3">Real-Time Infrastructure in Symphony</h3>
<p class="casestudy-text">
As we have seen, both WebRTC and WebSocket have trade-offs that make each better for some use cases and less
suitable
for others. When designing Symphony, we thought about which of these two technologies would be better for our
use case.
</p>
<p class="casestudy-text">
Applications that would use Symphony are likely already using the <a
href="https://www.liquidweb.com/blog/client-server-architecture/">client-server model</a>, which lies at the
heart
of modern
web applications. As a protocol based on a client-server model, WebSocket was therefore a natural
choice for us.
</p>
</div>
</div>
<!-- Section 3 -->
<div id="section-3" class="section">
<h2 class='section-heading' data-section="3">Collaboration</h2>
<p class='casestudy-text'>
Now that we’ve established what “real-time” means and how it can be achieved over the web, let’s turn to
collaboration.
What do we mean by “collaboration,” and what are some of its components?
</p>
<p class='casestudy-text'>
First, let’s consider a simple chat application. Imagine we have two users, Alice and Bob, exchanging messages
in
real-time. As soon as one user enters a message, the other user sees the message displayed without noticeable
delay. Is
this an example of a collaborative application?
</p>
<div class="diagram">
<video autoplay loop muted playsinline>
<source src="assets/videos/chat-app.mp4" type="video/mp4" />
Your browser does not support the HTML5 Video element.
</video>
</div>
<p class='casestudy-text'>
The answer is <strong>no</strong>. While the two users are exchanging information in real-time, they are not
collaborating because they are not modifying
the <a href="https://www.figma.com/blog/how-figmas-multiplayer-technology-works/">same state</a>. Each user has
control only over his or her own messages, and each of these messages is
independent
of
the others. Alice can see Bob’s messages in real-time, but she cannot modify them.
</p>
<p class="casestudy-text">
Now let’s look at a different kind of application.
</p>
<div class="diagram">
<video autoplay loop muted playsinline>
<source src="assets/videos/word-processor.mp4" type="video/mp4" />
Your browser does not support the HTML5 Video element.
</video>
</div>
<p class="casestudy-text">
This time, Alice and Bob are using an online word processor to work together on a document. Alice and Bob are
each
making their own changes, but they are viewing the same document, reflecting the modifications made by both
users
to
that document, in real-time. Therefore we can say that there exists a <strong>shared state</strong> between
Alice and Bob.
</p>
<p class="casestudy-text">
This shared state, which is reflected in a single synchronized view presented to all users working on the same
document
or project, lies at the core of real-time collaborative applications.
</p>
<p class="casestudy-text">
Let’s now review some concepts that are critical to understanding how online collaboration works.
</p>
<div id="subsection-3.1" class="subsection">
<h3 class="subsection-heading" data-subsection="3.1">Rooms</h3>
<div class="diagram">
<img src="assets/images/diagrams/rooms.png" class="w-full" />
</div>
<p class="casestudy-text">
Collaboration is something that happens when two or more users work together on a specific document or
project.
We call
this group of collaborating users, together with the shared state they are collaborating on, a
<strong>room</strong>. This is consistent with the nomenclature <a
href="https://liveblocks.io/docs/concepts#Room">favored by others</a> in this space including
Liveblocks, a provider of real-time
collaboration as a service:
</p>
<blockquote class="blockquote">
"A room is the space people can join to collaborate together."" - Liveblocks
</blockquote>
<p class="casestudy-text">
A <strong><a href="https://docs.yjs.dev/">document</a></strong> is the shared state that users are
collaborating on. Keep in mind that
collaboration is <strong>not limited to text documents</strong>; a document could also be a JavaScript Map
with key/value pairs that represent drawn objects on a whiteboard, for example.
</p>
</div>
<div id="subsection-3.2" class="subsection">
<h3 class="subsection-heading" data-subsection="3.2">Presence</h3>
<p class="casestudy-text">
When collaborating with others, it is useful to see not only the current state of the document but also which
users are
currently present and what they are doing at any given moment. Seeing what other users are doing in real-time
makes for
a smoother and more meaningful collaborative experience.
</p>
<div class="diagram">
<img src="assets/images/diagrams/whiteboard-awareness.png" />
</div>
<p class="casestudy-text">
We call this feature <strong>presence</strong>, which is the term most commonly used by other providers of
real-time collaboration tools, including Liveblocks.
In Symphony, each client sends an update to the server when some aspect of presence–cursor position for
example–
has
changed. Which data to include in these presence updates, and the way that data is structured, is left to the
application developer.
</p>
<p class="casestudy-text">
It should be noted that presence data is distinct from the shared state (document) that is the subject of
users’
collaboration. Importantly, presence data is ephemeral and does not need to be persisted in the backend.
</p>
</div>
<div id="subsection-3.3" class="subsection">
<h3 class="subsection-heading" data-subsection="3.3">Undo/Redo</h3>
<p class="casestudy-text">
Another concept worth discussing when it comes to collaborative applications is the ability of users to
<strong>undo</strong> (and/or <strong>redo</strong>) their updates.
</p>
<p class="casestudy-text">
When only a single user is editing, the concept of undo is simple: simply revert to the state immediately
preceding the
most recent change.
</p>
<div class="diagram">
<video autoplay loop muted playsinline>
<source src="assets/videos/whiteboard-undo.mp4" type="video/mp4" />
Your browser does not support the HTML5 Video element.
</video>
</div>
<p class="casestudy-text">
However, when multiple users are making changes, the picture gets more complicated. If a user’s undo action
simply
returns the state to that immediately preceding that user’s last update, what happens to intervening updates
by
others?
</p>
<div class="diagram">
<video autoplay loop muted playsinline>
<source src="assets/videos/whiteboard-undo-collab-bad.mp4" type="video/mp4" />
Your browser does not support the HTML5 Video element.
</video>
</div>
<p class="casestudy-text">
When Bob clicks the undo button, Alice’s red triangle is deleted. That’s probably not what either person
wanted!
</p>
<p class="casestudy-text">
So going back to the state preceding the user’s change is not the answer. What we want instead is to make the
undo/redo
behavior user-specific, with the scope of these actions limited to the user’s own changes.
</p>
<div class="diagram">
<video autoplay loop muted playsinline>
<source src="assets/videos/whiteboard-undo-collab-good.mp4" type="video/mp4" />
Your browser does not support the HTML5 Video element.
</video>
</div>
<p class="casestudy-text">
When Bob clicks “Undo”, his color change to the circle is reverted, but Alice’s red triangle remains in place.
</p>
</div>
<div id="subsection-3.4" class="subsection">
<h3 class="subsection-heading" data-subsection="3.4">Conflict and Resolution</h3>
<p class="casestudy-text">
Turning back to Alice and Bob’s document, let’s imagine that Alice and Bob make different changes to the same
content at
the same time. What might happen then?
</p>
<div class="diagram">
<video autoplay loop muted playsinline>
<source src="assets/videos/word-processor-out-of-order-same-time.mp4" type="video/mp4" />
Your browser does not support the HTML5 Video element.
</video>
</div>
<p class="casestudy-text">
Here, Alice and Bob have different ideas for what a good title would be, and they each transmit a change to
the
title at
the same time. Next, Bob’s change is delivered to Alice, which overrides the state presented in her client–she
now sees
Bob’s version. Meanwhile, Alice’s change is delivered to Bob, which overrides his local state, and he is now
shown
Alice’s version. This means that Alice and Bob are out of sync, and we’ve lost the critical underpinning of
real-time
collaboration - a shared state.
</p>
<p class="casestudy-text">
Another source of conflict in collaborative applications is out-of-order delivery of messages.
</p>
<p class="casestudy-text">
In a non-collaborative application like a chat program, this is generally not a critical problem.
</p>
<div class="diagram">
<video autoplay loop muted playsinline>
<source src="assets/videos/chat-app-random-order.mp4" type="video/mp4" />
Your browser does not support the HTML5 Video element.
</video>
</div>
<p class="casestudy-text">
Turning back to Alice and Bob’s document, though, what happens if Alice makes some changes, but due to network
conditions these updates are delivered to Bob out of order?
</p>
<div class="diagram">
<video autoplay loop muted playsinline>
<source src="assets/videos/word-processor-out-of-order.mp4" type="video/mp4" />
Your browser does not support the HTML5 Video element.
</video>
</div>
<p class="casestudy-text">
Here, Alice makes two changes to the title, but due to network conditions the changes are delivered to Bob in
reverse
order. Bob’s state now reflects the first change, while Alice’s state reflects the second. Again, the users
find
themselves out of sync.
</p>
<p class="casestudy-text">
It’s acceptable - even inevitable - for users to go out of sync temporarily, but we need some way of ensuring
that they
will eventually converge again to the same state. In the end, we want all users to see the same document
on their
screen, reflecting all the changes that have been made.
</p>
<p class="casestudy-text">
So how can we deal with this problem? There are generally two families of algorithms that are used to resolve
conflicts
in collaborative applications.
</p>
<h5 class="mini-heading">Operational Transformation</h5>
<p class="casestudy-text">
<strong>Operational transformation (OT)</strong> is the traditional technique to synchronize and reconcile
concurrent changes to a shared data structure in real-time
collaborative applications. When a user inserts or deletes data, that change is first sent to the server. The
server
then modifies, or transforms, that change if necessary, before sending it on to other users.
</p>
<p class="casestudy-text">
Used most famously by Google Docs, OTs are especially good for text editing of long documents with low memory
and
performance overhead. They do have downsides, however. They generally require that a user be connected to
the
server at all times, <a href="https://www.tiny.cloud/blog/real-time-collaboration-ot-vs-crdt/">limiting</a>
the user’s ability to work offline and sync up later after connecting. Most
importantly, they are <a href="https://www.figma.com/blog/how-figmas-multiplayer-technology-works/">very hard
to implement correctly</a>. It is difficult to deal with all edge cases, and
many
papers on OT implementations were found to have errors, years after publication.
</p>
<blockquote class="blockquote">
"Unfortunately, implementing OT sucks. There's a million algorithms with different tradeoffs, mostly
trapped
in academic papers. The algorithms are really hard and time consuming to implement correctly." -
Joseph Gentle, former Google Wave engineer
</blockquote>
<h5 class="mini-heading">Conflict-Free Replicated Data Types</h5>
<p class="casestudy-text">
A <strong>Conflict-Free Replicated Data Type (CRDT)</strong> is a type of data structure that is designed to
be
replicated across multiple devices. CRDTs have mathematical
properties that guarantee eventual consistency, even in the presence of concurrent, conflicting updates.
</p>
<p class="casestudy-text">
A relatively new concept aiming to solve problems in distributed computing, <a
href="https://exaspark.medium.com/top-5-ways-to-implement-real-time-rich-text-editor-ranked-by-complexity-3bc26e3c777f">
CRDTs have become a popular
choice
</a> in
resolving merge conflicts arising out of real-time collaboration, which can be seen as a kind of distributed
system
where state is shared across multiple clients.
</p>
<p class="casestudy-text">
Unlike OT, CRDTs do not make any assumptions about your network topology–they can be used in a strictly
peer-to-peer
environment, but also work <a
href="https://exaspark.medium.com/top-5-ways-to-implement-real-time-rich-text-editor-ranked-by-complexity-3bc26e3c777f">perfectly
well in a client-server model</a>.
</p>
<blockquote class="blockquote">
"Even if you have a client-server setup, CRDTs are still worth researching because they provide a
well-studied, solid foundation to start with."" - Evan Wallace, Figma
</blockquote>
<p class="casestudy-text">
Another key characteristic of CRDTs not shared by OT is partition fault tolerance. In a distributed system,
network
partitions can occur due to congestion or hardware failures. With CRDTs, even if two disconnected users update
the same
content concurrently, the updates will eventually merge when the partition is resolved.
</p>
<p class="casestudy-text">
Early CRDTs were found to be relatively slow compared to their <a
href="https://josephg.com/blog/crdts-are-the-future/">OT counterparts</a>. Another potential
downside is
memory overhead, since every change to a document creates more data that must be retained to guarantee
consistency.
</p>
<blockquote class="blockquote">
"Because of how CRDTs work, documents grow without bound. … Can you ever delete that data? Probably not.
And
that data can’t just sit on disk. It needs to be loaded into memory to handle edits." - Joseph
Gentle,
former Google Wave engineer
</blockquote>
<p class="casestudy-text">
<a href="https://www.youtube.com/watch?v=x7drE24geUw">Recent work by Martin Kleppmann</a> and others has made
CRDTs faster and less memory hungry. These
advancements have
led CRDTs to overtake OT as the tool of choice for real-time collaboration in the <a
href="https://josephg.com/blog/crdts-are-the-future/">
estimation of some
researchers.
</a>
</p>
<h5 class="mini-heading">Conflict Resolution in Symphony</h5>
<p class="casestudy-text">
So which choice is best?
</p>
<p class="casestudy-text">
The first thing to point out is that there is no single best, one-size-fits-all solution to resolving merge
conflicts.
The best choice will, therefore, depend on the specific use case.
</p>
<p class="casestudy-text">
For Symphony, we were looking for something that was open-source, well-documented, and performant. We also
wanted a
solution that would allow users to work offline. These factors led us to select CRDTs due to their robust
open-source
implementations, recent performance improvements, and tolerance for network partitions.
</p>
<p class="casestudy-text">
For the specific CRDT implementation we chose <strong><a
href="https://www.tag1consulting.com/blog/yjs-webrtc-part-5">Yjs</a></strong>, a mature library with
an active community and tools ecosystem. Yjs has also been battle-tested in production applications
including:
</p>
<ul class="casestudy-list">
<li class="casestudy-list-item">
<a href="https://room.sh/">Room.sh</a>, meeting spaces with collaborative drawing, editing, and coding
</li>
<li class="casestudy-list-item">
<a href="https://nimbusweb.me/note/">Nimbus Note</a>, team collaboration software
</li>
<li class="casestudy-list-item">
<a href="https://dynaboard.com/">Dynaboard</a>, a tool for collaborative web development
</li>
</ul>
<p class="casestudy-text">
We also considered using Automerge, the other leading open-source offering in this space. There was probably
not
a wrong
choice to be made between the two, but we were ultimately persuaded by Yjs’s superior <a
href="https://github.com/dmonad/crdt-benchmarks">maturity and speed</a>,
as well
as its <a href="https://news.ycombinator.com/item?id=34586433">relative memory efficiency</a>.
</p>
</div>
</div>
<!-- Section 4 -->
<div id="section-4" class="section">
<h2 class='section-heading' data-section="4">Solutions</h2>
<p class='casestudy-text'>
Now that we understand the ingredients of real-time collaboration, let’s place ourselves in the shoes of a
developer who
wishes to add real-time collaboration to an application. What kinds of functionality and infrastructure will we
need?
</p>
<p class='casestudy-text'>
First, we’ll want a WebSocket server or servers to receive updates from users and transmit those updates to
other users
who are in the same room.
</p>
<p class='casestudy-text'>
Next, we’ll need a means of resolving merge conflicts.
</p>
<p class='casestudy-text'>
Ideally, we will also have a way of persisting room state. When users leave a room, they probably don’t want to
lose all
of their work. Instead, they should be able to return later and pick up where they left off. This can be
achieved by
storing room state in a database when users disconnect, and retrieving that state from the database when they
reconnect.
</p>
<p class='casestudy-text'>
What if a user loses their internet connection but wants to continue working on a document? Wouldn’t it be nice
if a
user’s offline changes can be stored and then synced back up with others when the user reconnects? To enable
this, we’ll
want a way for each client to store state locally.
</p>
<p class='casestudy-text'>
Finally, from the developer’s perspective, it could be useful to know how users are collaborating in an
application. A
developer dashboard might be one way of gaining visibility into metrics like the number of rooms, the number of
users
per room, and the size of the document associated with a given room.
</p>
<p class="casestudy-text">
What then are the developer’s options for obtaining this functionality and related infrastructure?
</p>
<div id="subsection-4.1" class="subsection">
<h3 class="subsection-heading" data-subsection="4.1">Do-It-Yourself (DIY)</h3>
<p class='casestudy-text'>
One option is for the developer to implement or source each of these components separately and stitch them
together.
</p>
<p class='casestudy-text'>
For the backend infrastructure to handle real-time messaging and persist state, one could use a
backend-as-a-service
such as Ably, Pusher, or PubNub, or alternatively deploy one’s own backend by provisioning resources on a
cloud provider
like AWS.
</p>
<p class='casestudy-text'>
For conflict resolution, the developer would most likely need to research and integrate an existing
open-source
solution. She might even choose to implement a bespoke conflict resolution implementation, although this would
be
considerably more time-consuming and difficult.
</p>
<p class="casestudy-text">
In sum, going the DIY route is less costly, but the implementation burden on the developer in terms of
sourcing and
integrating the needed components is higher than using one of the services described in the next section.
</p>
</div>
<div id="subsection-4.2" class="subsection">
<h3 class="subsection-heading" data-subsection="4.2">Real-Time Collaboration as a Service</h3>
<p class='casestudy-text'>
Building out your own backend to manage websocket connections and implementing or integrating a means of
conflict
resolution <a href="https://fluidframework.com/docs/">can be challenging</a>. For this reason, services and
frameworks exist to abstract away the backend and
conflict
resolution and let the developer focus on the application itself.
</p>
<blockquote class="blockquote">
"Because building low-latency, collaborative experiences is hard!" - Microsoft
</blockquote>
<div id="subsection-4.2.1" class="subsection">
<h4 class="subsubsection-heading" data-subsection="4.2.1">Liveblocks</h4>
<p class="casestudy-text">
Liveblocks is a major player in this space that provides an all-in-one solution for developers wishing to
add real-time
collaboration to their applications. A service that takes care of managing and scaling the WebSocket
infrastructure,
Liveblocks also provides a custom CRDT-like solution for resolving merge conflicts. This convenience comes
at a cost,
however: for an application with up to 2,000 monthly active users, <a
href="https://liveblocks.io/pricing">the price is</a> $299 as of this writing.
</p>
</div>
<div id="subsection-4.2.2" class="subsection">
<h4 class="subsubsection-heading" data-subsection="4.2.2">Fluid Framework</h4>
<p class="casestudy-text">
Fluid Framework is an open-source, cloud-agnostic framework for real-time collaboration, developed by
Microsoft. For
conflict resolution, it uses a distributed data structure that Microsoft describes as <a
href="https://fluidframework.com/docs/faq/">
“more similar to CRDT
than OT”
</a>. When deployed on a single server, Fluid Framework can handle hundreds of concurrent users. For
larger
applications, however, the developer will need to implement <a
href="https://fluidframework.com/docs/faq/">her own scaling solution</a>. Another drawback of
Fluid
Framework is that it does not include support for offline editing, although short periods of client
disconnection are
<a href="https://learn.microsoft.com/en-us/azure/azure-fluid-relay/resources/faq">tolerated</a>.
</p>
<p class="casestudy-text">
While Fluid Framework itself is open-source and self-hosted, Microsoft also offers a managed service called
Azure Fluid
Framework that uses Fluid Framework under the hood.
</p>
</div>
</div>
<div id="subsection-4.3" class="subsection">
<h3 class="subsection-heading" data-subsection="4.3">Where Does Symphony Fit In?</h3>
<p class='casestudy-text'>
We drew inspiration from both Liveblocks and Fluid Framework. Our goal was to build a framework that
developers can
easily integrate into existing applications to add real-time collaborative functionality. We also wanted to
make
Symphony available to developers free of charge as an open-source project, and to enable offline editing
functionality
so that developers could achieve a better experience for their users.
</p>
<p class='casestudy-text'>
Additionally, we decided to implement a dashboard to address a couple of potential problems developers might
face after
deployment.
</p>
<p class='casestudy-text'>
First, given the memory requirements of CRDTs and the considerable CPU demands placed on the server as it
receives and
propagates updates, system limits could be exceeded if not actively monitored. The dashboard provides
developers with
visibility into metrics such as number of rooms, sizes of documents stored in rooms, and numbers of
connections per room
that will help identify when and why performance problems occur.
</p>
<p class="casestudy-text">
Second, the dashboard can be used to view the current state of a room in JSON format. This can serve as a
debugging tool
when the developer’s application is not storing and modifying state as expected.
</p>
<p class="casestudy-text">
In short, Symphony is aimed at developers of collaborative web applications who want to get real-time
collaboration up
and running quickly on a smaller budget.
</p>
<div class="diagram">
<img src="assets/images/diagrams/solutions.png" class="w-full" />
</div>
</div>
</div>
<!-- Section 5 -->
<div id="section-5" class="section">
<h2 class="section-heading" data-section="5">Building Symphony</h2>
<div id="subsection-5.1" class="subsection">
<h3 class="subsection-heading" data-subsection="5.1">Design Philosophy</h3>
<p class="casestudy-text">
When designing Symphony, our decisions were informed at every step of the way by the fundamental problem we
aimed to
address: that building collaborative applications where multiple users can change a given piece of state
concurrently is
difficult.
</p>
<p class="casestudy-text">
With this in mind, we aimed to provide the infrastructure needed for creating conflict-free collaborative
experiences on
the web in a way that is easy for developers to drop into their existing applications.
</p>
<p class="casestudy-text">
We wanted to abstract away the complexity of managing and scaling WebSocket connections, resolving merge
conflicts, and
persisting state so that developers would be free to focus on their applications.
</p>
<p class="casestudy-text">
Finally, we wanted the framework to provide capacity comparable to the highest level of Liveblocks’ Pro tier,
which supports up to 25,000 monthly active users. Assuming 10%-30% of MAUs online at any given time, and
choosing to err on the high side, we arrived at a goal of 8,000 concurrent active users.
</p>
</div>
<div id="subsection-5.2" class="subsection">
<h3 class="subsection-heading" data-subsection="5.2">Prototyping</h3>
<p class="casestudy-text">
Our first goal when building Symphony was to create a basic prototype of the system. We chose to do this in
AWS, which
was a natural choice as the biggest player in the cloud provider market with 32% market share as of early
2023.
</p>
<div id="subsection-5.2.1" class="subsection">
<h4 class="subsubsection-heading" data-subsection="5.2.1">Deploying the WebSocket Server</h4>
<p class="casestudy-text">
Since we are using the WebSocket protocol for real-time data transfer, we will need a WebSocket server to
receive and
distribute messages. And since we have chosen the Yjs CRDT implementation, our starting point for the
Symphony backend
was y-websocket, a reference WebSocket server that implements the <a
href="https://github.com/yjs/y-websocket">Yjs protocols</a> for syncing updates and
broadcasting
presence. We deployed the server to Amazon Elastic Compute Cloud (EC2), which is the standard virtual
private