A simple real-time collaborative whiteboard application built with Node.js, Express, and Socket.IO. It is meant to be used as a wizard of Oz, by being overlayed over a live video feed of parking lots.
Follow these steps to get the application running on your local machine:
- Node.js and npm (Node Package Manager) installed. You can download them from https://nodejs.org/.
git clone https://github.com/ricardojaratrepat/g5_eis.gitcd g5_eisnpm install(This will install express and socket.io as defined in the package.json)
node server.js- Open your web browser and navigate to
http://localhost:8000. - To see the collaborative features, you can open the same URL in multiple browser tabs or different browsers.
g5_eis/
├── public/ # Static files served to the client
│ ├── index.html # Main HTML structure
│ ├── style.css # CSS for styling
│ └── client.js # Client-side JavaScript logic
├── server.js # Node.js server with Express and Socket.IO logic
├── package.json # Project metadata and dependencies
└── README.md # This fileThe primary data structure in this application is the Drawing Object. Each action on the whiteboard (drawing a line segment or erasing) is represented as an object.
This object is created when a user draws a continuous line or drags the eraser.
- Sent by Client & Broadcast by Server
{
x0: Number, // Starting X coordinate of the line segment
y0: Number, // Starting Y coordinate of the line segment
x1: Number, // Ending X coordinate of the line segment
y1: Number, // Ending Y coordinate of the line segment
color: String, // Color of the line (e.g., 'lightgreen' for draw, '#FFFFFF' for erase)
width: Number, // Width of the line (or size of the eraser path)
isEraser: Boolean // true if this is an erasing action, false for drawing
}This object is created when a user clicks with the eraser tool (not when he drags).
- Sent by Client & Broadcast by Server
x: Number, // Top-left X coordinate of the erase square
y: Number, // Top-left Y coordinate of the erase square
size: Number, // Size (width and height) of the erase square
color: String, // Color of the erase (e.g., '#FFFFFF')
isEraser: Boolean // Always true for this typeThe server maintains an array called drawingHistory. Each element in this array is an object that wraps the drawing/erasing data with its type:
// Example element in drawingHistory array
{
type: 'NEW_DRAWING', // or 'ERASE_DRAWING'
payload: { /* ... drawing or erasing object as defined above ... */ }
}Communication between the client and server is handled via Socket.IO. A generic message event is used on the server-side for receiving client actions.
Clients send messages to the server using a common structure:
- Event Name:
message - Data Sent:
{
type: String, // The type of action (e.g., 'NEW_DRAWING', 'ERASE_DRAWING')
payload: Object // Data specific to the action type (see Data Structures above)
}The server.js uses a messageHandlers object to process incoming messages based on their type:
const messageHandlers = {
NEW_DRAWING: (socket, payload) => { /* ... */ },
ERASE_DRAWING: (socket, payload) => { /* ... */ },
REQUEST_HISTORY: (socket, payload) => { /* ... */ }
};
// Inside io.on('connection', ...):
socket.on('message', (message) => {
const { type, payload } = message;
const handler = messageHandlers[type];
if (handler) {
handler(socket, payload); // socket is passed for context (e.g., broadcasting)
}
});You can see specific message implementations over at messageHandlers.