Build a Real‑Time Collaborative Whiteboard with WebSocket, Canvas & Webman
This tutorial walks through creating a scalable, infinite‑size collaborative whiteboard that synchronizes drawing actions across multiple browsers in real time using WebSocket, Canvas, and the Webman PHP framework, covering server setup, front‑end integration, and full code examples.
Overview
This guide shows how to build a collaborative whiteboard using HTML5 Canvas for rendering and WebSocket (via Workerman/Webman) for real‑time bidirectional communication. The board automatically expands to the browser viewport, allowing multiple users to draw simultaneously with instant synchronization.
Prerequisites
PHP 8.0+ with Composer installed
Webman framework (which bundles Workerman)
A modern desktop browser that supports Canvas and WebSocket
Install the template engine
Webman uses a view handler. Install the ThinkPHP template package and configure the handler: composer require topthink/think-template Modify config/view.php:
<?php
use support\view\ThinkPHP;
return [
'handler' => ThinkPHP::class,
];
?>HTTP Service
Create a controller that returns the canvas view.
<?php
declare(strict_types=1);
namespace app\controller;
use support\Request;
use support\Response;
class DemoController
{
public function canvas(Request $request): Response
{
return view('demo/canvas');
}
}
?>Place the view file at app/view/demo/canvas.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Collaborative Whiteboard</title>
</head>
<body>
<canvas id="canvas" style="border:1px solid #000;">Your browser does not support Canvas.</canvas>
<script src="/static/js/index.js"></script>
</body>
</html>Front‑end Logic
Save the script as /static/js/index.js. It resizes the canvas to the full viewport, captures mouse events, sends drawing points through WebSocket, and renders incoming points from other users.
var canvas = document.getElementById('canvas');
canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
var ctx = canvas.getContext('2d');
var isDrawing = false;
var ws = new WebSocket('ws://127.0.0.1:8788'); // adjust IP if needed
ws.onopen = function () {
canvas.onmousedown = function (e) {
isDrawing = true;
ctx.moveTo(e.clientX, e.clientY);
sendPoint(e, 1);
};
canvas.onmousemove = function (e) {
if (isDrawing) {
ctx.lineTo(e.clientX, e.clientY);
ctx.stroke();
sendPoint(e, 2);
}
};
canvas.onmouseup = function () { isDrawing = false; };
};
ws.onmessage = function (e) {
var data = JSON.parse(e.data);
if (data.type === 1) {
ctx.moveTo(data.x, data.y);
} else if (data.type === 2) {
ctx.lineTo(data.x, data.y);
ctx.stroke();
}
};
function sendPoint(e, type) {
var point = {type: type, x: e.clientX, y: e.clientY};
ws.send(JSON.stringify(point));
}WebSocket Service
Define a custom Webman process that broadcasts any received message to all connected clients.
<?php
declare(strict_types=1);
namespace process;
use Workerman\Connection\TcpConnection;
class CanvasWebsocket
{
public function onConnect(TcpConnection $connection)
{
echo "onConnect
";
}
public function onWebSocketConnect(TcpConnection $connection, $http_buffer)
{
echo "onWebSocketConnect
";
}
public function onMessage(TcpConnection $connection, $data)
{
foreach ($connection->worker->connections as $client) {
if ($client !== $connection) {
$client->send($data);
}
}
}
public function onClose(TcpConnection $connection)
{
echo "onClose
";
}
}
?>Add the process to config/process.php:
return [
// ... other process definitions ...
'canvas_websocket' => [
'handler' => \process\CanvasWebsocket::class,
'listen' => 'websocket://0.0.0.0:8788',
'count' => 1,
],
];Running the Application
Start the Webman server (e.g., php start.php or the command defined in your project).
Open http://localhost/demo/canvas in two or more browser windows.
Draw on any canvas; strokes appear instantly in all other windows.
Key Points
The canvas size is set to the full browser viewport, effectively providing an “infinite” drawing area.
Mouse coordinates are sent as JSON objects with a type field (1 = move, 2 = draw) to differentiate actions.
The WebSocket process uses Workerman’s connection pool to forward each message to every other client, achieving real‑time collaboration.
Adjust the WebSocket URL ( ws://IP:8788) to match the server’s network interface.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Open Source Tech Hub
Sharing cutting-edge internet technologies and practical AI resources.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
