Asopitech With · デザインツール
Figma マルチプレイヤー with Alopex Chirps
Figmaのマルチプレイヤーは、デザインツールのFigmaで、複数の人が同じデザインを同時に編集できる仕組みです。これをAlopex Chirpsを応用して再現すると…?
更新: 2026/10/13
どんなサービスか
Figmaでは、同じデザインの文書を、複数の人が同時に編集できます。Figmaの技術ブログによると、クライアントはWebSocketでサーバーのクラスタにつながり、文書ごとに別のプロセスが動き、その文書を編集する全員がそこにつながります。サーバーは、オブジェクトのプロパティごとに最後に届いた値を持ちます。扱うデータは、文書のオブジェクトの木と、各人のカーソルと選択です。
通信の組み立て
- ノードのメンバーシップ:文書を受け持つサーバーをノードとしてメッシュに入れます。どのノードが動いているかを、gossipで全ノードが知ります。
- 編集の転送:別のノードにつながった人の編集は、その文書を受け持つノードへ、宛先を指定して送ります。
- カーソルの配信:カーソルや選択の位置は、古くなってもかまわない一時的な配信方式で送ります。編集の通信と分けて流れます。
- ノードの離脱:文書を受け持つノードが止まると、離脱のイベントが届き、別のノードで文書を開き直せます。
どの文書をどのノードが受け持つか、プロパティの値をどう決めるかは、アプリのコードです。
配置の形
サーバーの各ノードにChirpsを組み込みます。ノードどうしは、QUICと相互TLSでつながります。
Alopex Chirpsが合う理由
共同編集のサーバーは、確実に届けたい編集と、間引いてもかまわないカーソルの、性質の違う2つの流れを運びます。Chirpsは、配信方式を分けて送れ、ノードの増減もgossipで知らせます。
コードで見る
ノードをまたぐ編集に一貫した順序を与える、ハイブリッド論理時計の中心だけを取り出したコードです(Rust)。
use alopex_chirps_gossip_swim::hlc::{Clock, LocalHlc};
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use std::time::Duration;
// 説明用の手動クロック(実際はシステム時計)
struct ManualClock(AtomicU64);
impl Clock for ManualClock {
fn now_millis(&self) -> u64 {
self.0.load(Ordering::SeqCst)
}
}
fn main() {
let skew = Duration::from_secs(1);
// 文書を受け持つ 2 つのノード A / B。それぞれ論理時計を持つ
let mut a = LocalHlc::with_clock(skew, Arc::new(ManualClock(AtomicU64::new(100))));
let mut b = LocalHlc::with_clock(skew, Arc::new(ManualClock(AtomicU64::new(90))));
// A 側で編集が起きるたびに時計を進める
let edit1 = a.tick();
let edit2 = a.tick();
println!("A: edit1={edit1:?} edit2={edit2:?}");
// A の編集を B が受け取ると、B の時計は A を追い越す
let seen = b.receive(edit2).expect("receive");
println!("B receives A: {seen:?}");
// その後 B 側で起きた編集は、必ず A の編集より後に順序づく
let b_edit = b.tick();
println!("B next: {b_edit:?}");
assert!(b_edit > edit2);
println!("OK: どのノードも、オブジェクトの編集を同じ順序で適用できる");
}ノードをまたぐ編集に一貫した順序がつくので、プロパティの最後の値が全員で一致します。編集とカーソルの配信方式の出し分け(確実な配送と間引き)は、本文のとおりChirpsが受け持ちます。
編集とカーソルを配信方式で分けてノードどうしに送れるAlopex Chirpsで、共同編集のサーバーを作ってみよう。GitHubのクイックスタートから始められます。