Xây Virtual Classroom & Collaborative Whiteboard với Canvas/WebRTC
Hướng dẫn xây collaborative whiteboard cho lớp học ảo bằng Canvas, WebSocket, CRDT và WebRTC, hỗ trợ đồng bộ nét vẽ, video SFU và phục hồi phiên học.
ShanverseCập nhật 10 phút đọc— lượt xemĐang tải lượt xem
WebRTC
Collaborative Editing
EdTech
1. Bài toán thực tế
Một lớp học ảo thường có hai luồng thời gian thực rất khác nhau:
Media: camera, microphone, chia sẻ màn hình; băng thông lớn, chấp nhận mất một số gói tin nhưng cần độ trễ thấp.
Collaboration data: nét bút, hình học, văn bản, con trỏ, quyền chỉnh sửa; dữ liệu nhỏ hơn nhưng cần thứ tự, định danh, chống trùng và khả năng khôi phục.
Canvas chỉ là lớp render. WebRTC chỉ là phương tiện truyền media/data giữa các endpoint. Trạng thái bảng, quy tắc xung đột, lưu trữ và phân quyền vẫn phải được thiết kế riêng.
2. Chọn kiến trúc truyền tải
Không dùng full-mesh WebRTC cho cả lớp: với n người, số kết nối tăng xấp xỉ n(n-1)/2
Bình luận
. Media nhiều bên nên đi qua
SFU
. Whiteboard nên đi qua một collaboration gateway có trạng thái phòng rõ ràng; RTCDataChannel chỉ phù hợp cho tối ưu đặc biệt, không nên là nguồn sự thật duy nhất.
3. Kiến trúc tổng thể
Mermaid
Loading diagram…
Classroom Service: lớp, lịch học, thành viên, vai trò.
Collaboration Gateway: xác thực socket, kiểm tra quyền, chuẩn hóa operation, broadcast.
Room Router: đưa mọi operation của một board về đúng owner/shard.
Event Log: lưu operation bền vững để replay và audit.
Snapshot Worker: định kỳ tạo snapshot, rút ngắn thời gian join lại.
SFU: chuyển tiếp media; không gánh logic bảng trắng.
4. Mô hình dữ liệu: đồng bộ đối tượng, không đồng bộ pixel
Đừng gửi ảnh chụp Canvas sau mỗi lần thay đổi. Hãy lưu semantic objects:
Với nét tự do, thu thập points {x, y, pressure, t} trong hệ tọa độ logic độc lập với zoom và device pixel ratio. Gửi theo lô mỗi 16–40 ms hoặc khi đủ số điểm, thay vì gửi từng pointermove.
Một nét đã hoàn thành nên bất biến. Undo tạo operation xóa/tombstone thay vì sửa lịch sử. Hình học và text có thể cập nhật bằng version hoặc CRDT shared types.
5. Envelope và operation protocol
Server không tin actorId, role hoặc timestamp do client gửi. Danh tính lấy từ session đã xác thực. Mỗi operation cần operationId để chống gửi lặp và sequence do server cấp để replay có thứ tự.
Phản hồi nên gồm:
6. Xử lý xung đột: CRDT ở nơi cần thiết
CRDT phù hợp khi nhiều người có thể sửa cùng trạng thái và cần hội tụ dù operation đến khác thứ tự. Yjs cung cấp shared types và awareness protocol, nhưng không có nghĩa mọi dữ liệu phải nằm trong một tài liệu khổng lồ.
Freehand stroke: append/chunk trong lúc vẽ; khi kết thúc trở thành object bất biến.
Text/sticky note: CRDT text để tránh ghi đè ký tự.
Transform hình: field-level last-writer-wins với logical clock, hoặc khóa mềm khi kéo.
Delete: tombstone thắng các patch cũ.
Z-order: dùng sortable position key thay vì đánh lại index của toàn bộ board.
Presence/cursor: ephemeral awareness, có TTL; không lưu vào lịch sử board.
Giới hạn kích thước document, tạo snapshot và compaction có kiểm soát. CRDT giải quyết hội tụ, không tự giải quyết authorization, quota, malware, moderation hay business rules.
7. Pipeline nét vẽ mượt trên Canvas
Dùng Pointer Events để hỗ trợ chuột, bút và cảm ứng trong một API.
Gọi setPointerCapture() sau pointerdown để không mất nét khi con trỏ ra ngoài canvas.
Đọc getCoalescedEvents() khi được hỗ trợ để lấy điểm chi tiết và vẽ đường mượt hơn.
Chuẩn hóa điểm về world coordinates.
Render local-first ngay lập tức; không đợi server echo.
Batch và gửi các chunk có số thứ tự.
Khi server từ chối, rollback hoặc đánh dấu object lỗi.
Dùng requestAnimationFrame cho render. Khi board lớn, áp dụng spatial index, dirty rectangles, layer cache và viewport culling. OffscreenCanvas + Web Worker có thể tách render/tessellation nặng khỏi main thread; luôn có fallback vì mức hỗ trợ khác nhau.
8. Join, reconnect và offline
Quy trình join an toàn:
Xác thực thành viên và quyền với boardId.
Tải snapshot gần nhất tại sequence S.
Replay operations sau S.
Đăng ký live stream từ sequence kế tiếp.
Chỉ hiển thị “đã đồng bộ” khi không còn gap.
Client lưu lastAppliedSequence. Khi reconnect, yêu cầu resumeFrom. Nếu log còn dữ liệu, server gửi phần thiếu; nếu đã compact, client tải snapshot mới. Pending operations cục bộ phải giữ nguyên operationId để retry idempotent.
Offline editing chỉ nên bật với scope rõ. Khi kết nối lại, validate lại quyền và schema; không tự động chấp nhận operation được tạo sau khi học sinh mất quyền chỉnh sửa.
9. WebRTC cho lớp học ảo
WebRTC cần signaling để trao đổi SDP/ICE. ICE dùng STUN để tìm đường trực tiếp và TURN để relay khi NAT/firewall chặn kết nối. Phải triển khai TURN production với credential ngắn hạn, TLS và theo dõi relay ratio.
Với nhiều người, dùng SFU:
Giáo viên publish camera/mic/screen một lần lên SFU.
Học sinh subscribe theo vai trò và layout.
Simulcast/SVC cho phép SFU chọn layer phù hợp băng thông.
Tắt video không nhìn thấy, ưu tiên audio và screen share.
Active-speaker và pagination giảm số stream đồng thời.
WebRTC mã hóa transport, nhưng SFU thường nhìn thấy media đã giải mã ở tầng xử lý. Nếu yêu cầu end-to-end encryption, cần thiết kế insertable streams, quản lý khóa và đánh đổi với recording/moderation.
10. Backpressure và slow consumers
Không để một thiết bị yếu làm đầy bộ nhớ gateway:
Giới hạn payload và số points mỗi chunk.
Gộp hoặc bỏ cursor events cũ; chỉ trạng thái mới nhất có giá trị.
Không bỏ durable operations đã accepted; ngắt và buộc client snapshot lại nếu queue vượt ngưỡng.
Theo dõi bufferedAmount với WebSocket/RTCDataChannel phía client.
Dùng binary encoding chỉ sau khi đo; JSON dễ debug hơn ở giai đoạn đầu.
Tách priority: quyền/board state > object operations > cursors. Asset upload đi kênh HTTPS riêng.
11. Phân quyền lớp học
Mỗi operation phải qua policy server-side:
Teacher: khóa/mở board, đổi trang, cấp quyền, xóa đối tượng, tạo breakout board.
Student editor: chỉ sửa trong vùng/trang được giao.
Viewer: chỉ nhận snapshot và events.
Không dựa vào việc ẩn nút trên UI. Token tham gia nên ngắn hạn, ràng buộc classroomId, boardId, role và session. Kiểm tra Origin, chống CSRF ở signaling/HTTP, rate limit join và operation, quét file tải lên, lọc URL ảnh ngoài.
Với học sinh nhỏ tuổi, tối thiểu hóa dữ liệu; đặt chính sách recording/retention, audit quyền truy cập và tránh hiển thị email hoặc định danh không cần thiết.
12. Lưu phiên học và xuất bản
Mô hình bền vững gồm:
Snapshot versioned của từng page.
Operation log append-only với actor đã xác thực.
Asset immutable có content hash.
Metadata: lớp, owner, thời gian, schema version.
Export PNG/PDF nên chạy server-side hoặc worker nền từ snapshot đã chốt, không chụp trạng thái Canvas ngẫu nhiên trên một client. Recording video và lịch sử whiteboard cần cùng timeline server để phát lại đồng bộ.
13. Mở rộng ngang
Shard theo boardId; một board có một logical owner tại một thời điểm. Gateway stateless có thể chuyển operation tới owner qua room router. Redis Pub/Sub chỉ phù hợp fan-out tạm thời; operation đã accepted phải nằm trong log bền vững như Redis Streams, Kafka hoặc database append-only.
Khi owner lỗi:
Lease hết hạn.
Owner mới đọc snapshot + log.
Tăng epoch của room.
Client reconnect/resume.
Operation từ epoch cũ bị từ chối hoặc định tuyến lại.
Sticky session là tối ưu, không phải điều kiện để đúng.
14. Quan sát và kiểm thử tải
Các chỉ số quan trọng:
p50/p95/p99 operation acknowledge và remote-render latency.
FPS, long tasks, memory và số object phía trình duyệt.
Kịch bản tải phải mô phỏng lớp học thật: 1 giáo viên, hàng trăm học sinh join gần đồng thời, 20–50 người vẽ cùng lúc, đổi trang, dán ảnh, reconnect storm và một node gateway/SFU bị lỗi. Kiểm thử cả mạng chậm, packet loss, tab nền và thiết bị cấu hình thấp.
15. Lộ trình triển khai
Canvas local với object model và undo/redo.
WebSocket gateway cho một room, idempotency và permission.
Snapshot + replay + reconnect.
CRDT cho text và object fields cần concurrent editing.
Presence/cursor tạm thời.
WebRTC audio/video qua SFU + TURN.
Sharding, durable log, load test và chaos test.
Recording, export, moderation và analytics.
Checklist production
Canvas là renderer, model không phụ thuộc pixel.
Operation có ID, schema version và server sequence.
Snapshot + replay không tạo gap khi chuyển sang live.
Retry idempotent; presence có TTL.
Teacher permissions được kiểm tra trên server.
Media dùng SFU; TURN đã kiểm thử trên mạng trường học/di động.
Outbound queues có giới hạn và chiến lược slow consumer.
Asset upload tách khỏi kênh collaboration.
Có retention, audit và bảo vệ dữ liệu trẻ vị thành niên.
Đã thử reconnect storm, failover và board lớn.
Kết luận
Kiến trúc bền vững là object model + Canvas rendering + WebSocket operation stream + snapshot/replay + CRDT có chọn lọc + WebRTC/SFU cho media. Khi tách media, collaboration state và persistence thành các lớp riêng, ứng dụng vừa giữ được cảm giác vẽ tức thì vừa mở rộng an toàn cho lớp học thật.
Thiết lập CSP Header an toàn cho Next.js App Router: directives quan trọng, nonce động, strict-dynamic, third-party scripts, reporting, kiểm thử và checklist production.
Hướng dẫn Image Compression và Canvas Resizing trên trình duyệt: thuật toán scale, TypeScript, định dạng WebP/JPEG/PNG, EXIF, CORS, Worker và bảo mật upload.