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.

Open Source Tech Hub
Open Source Tech Hub
Open Source Tech Hub
Build a Real‑Time Collaborative Whiteboard with WebSocket, Canvas & Webman

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaScriptCanvasPHPreal-time collaborationWebman
Open Source Tech Hub
Written by

Open Source Tech Hub

Sharing cutting-edge internet technologies and practical AI resources.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.