Rabikant Singh
Posted on November 20th
How to make a real-time drawing app with piesocket
"How to make a real-time drawing app with piesocket"
Create a New Vue Project
Open your terminal and run the following command to create a new Vue project:
npm create vue@latest
Follow the prompts to name your Vue app and configure it as you like.
Navigate to Your Vue App Folder: Change directory to your newly created Vue app:
Then cd into your vue app folder.
cd vue-example-app
Install Dependencies
Install the necessary dependencies for your Vue app to run:
npm install
Install Piesocket.js: Add Piesocket.js to your project for real-time communication features:
npm i piesocket-js@5
Set Up Piesocket
- Visit Piehost and create an account.
- Once logged in, create a new cluster.
- After creating the cluster, note down the clusterId and apiKey.

Create the Drawing Component: Navigate to the /src/components directory of your Vue app and create a new file named Drawing.vue.
Add Code to Drawing.vue: Open Drawing.vue and add the Vue component code that integrates Piesocket. This will include setting up the WebSocket connection using the clusterId and apiKey you obtained from Piehost.
const canvas = ref(null);
const state = reactive({
drawing: false,
context: null,
lastX: 0,
lastY: 0
});
- const canvas = ref(null);: This line declares a constant variable named canvas and initializes it with a ref object.
- const state = reactive({ ... });: Another constant variable named state is declared and assigned a reactive object. The reactive function is also from Vue.js and is used to create a reactive data object. It allows properties within the object to be tracked and updated automatically when their values change.
- Inside the state object, we have the following properties:
- drawing: A boolean flag that indicates whether the user is currently drawing on the canvas.
- context: = A reference to the 2D rendering context of the canvas. This context is used for drawing shapes, lines, and images.
- lastX and lastY: These variables likely store the last recorded coordinates (X and Y) of the user’s cursor or touch input on the canvas.
const startDrawing = (e) => {
state.drawing = true;
[state.lastX, state.lastY] = [e.offsetX, e.offsetY];
};
const draw = (e) => {
if (!state.drawing) return;
broadcastDraw(state.lastX, state.lastY, e.offsetX, e.offsetY);
drawLine(state.lastX, state.lastY, e.offsetX, e.offsetY);
[state.lastX, state.lastY] = [e.offsetX, e.offsetY];
};
const stopDrawing = () => {
state.drawing = false;
};
let broadcastDraw;
- startDrawing Function:
- This function is called when the user initiates drawing on a canvas.
- It sets the state.drawing flag to true, indicating that drawing is in progress.
- The [state.lastX, state.lastY] assignment captures the initial cursor position (X and Y coordinates) when the user starts drawing.
- draw Function:
- This function handles the actual drawing process.
- It checks whether state.drawing is true. If not, it returns early (no drawing occurs).
- The broadcastDraw function (which is not defined here) is called with the previous cursor position (state.lastX and state.lastY) and the current cursor position (e.offsetX and e.offsetY). This suggests that it might be broadcasting the drawing data to other clients or components.
- The drawLine function (also not defined here) is called to draw a line segment between the previous and current cursor positions.
- Finally, [state.lastX, state.lastY] is updated with the current cursor position.
- stopDrawing Function:
- This function is invoked when the user stops drawing.
- It sets state.drawing to false, indicating that drawing has ceased.
const pieSocket = new PieSocket({
clusterId: "s12117.nyc1",
apiKey: "0jlifmULE2eJFtCI2kjabsR7EdgaY4EWRZgdGMkE",
});
pieSocket.subscribe("drawing-room").then(ch => {
const channel = ch;
console.log("Channel is ready");
channel.listen("draw", (data) => {
const { x0, y0, x1, y1 } = JSON.parse(data);
drawLine(x0, y0, x1, y1);
});
broadcastDraw = (x0, y0, x1, y1) => {
channel.publish("draw", JSON.stringify({ x0, y0, x1, y1 }));
};
}).catch(error => {
console.error("Error subscribing to PieSocket channel:", error);
});
const drawLine = (x0, y0, x1, y1) => {
state.context.beginPath();
state.context.moveTo(x0, y0);
state.context.lineTo(x1, y1);
state.context.stroke();
};
onMounted(() => {
canvas.value.width = window.innerWidth;
canvas.value.height = window.innerHeight;
state.context = canvas.value.getContext('2d');
state.context.strokeStyle = "black";
state.context.lineJoin = 'round';
state.context.lineCap = 'round';
state.context.lineWidth = 10;
});
- pieSocket.subscribe("drawing-room"):
- This line initiates a subscription to a channel named “drawing-room” using the pieSocket object.
- The returned promise resolves with a channel (ch) that allows communication with other clients or components.
- Channel Setup:
- The channel variable is assigned the resolved value of the promise (i.e., the subscribed channel).
- A log message is printed to the console, indicating that the channel is ready.
- Listening for “draw” Events:
- PossiblyThe channel.listen("draw", (data) => { ... }) registers an event listener for the “draw” event.
- When a “draw” event occurs, the provided callback function is executed.
- Inside the callback, the data (presumably containing drawing coordinates) is parsed using JSON.parse.
- The drawLine function is then called with the extracted coordinates (x0, y0, x1, y1).
- broadcastDraw Function:
- The broadcastDraw function is assigned a new implementation.
- It takes four arguments: x0, y0, x1, and y1.
- Inside the function, it publishes a “draw” event to the channel, sending the coordinates as a JSON string.
- Error Handling:
- The .catch(error => { ... }) block handles any errors that occur during the subscription process.
- If an error occurs, an error message is logged to the console.
- drawLine Function:
- This function is responsible for drawing a line on the canvas.
- It begins a new path (state.context.beginPath()).
- Sets the starting point of the line to (x0, y0) using state.context.moveTo(x0, y0).
- Draws a line to the ending point (x1, y1) using state.context.lineTo(x1, y1).
- Finally, the line is rendered on the canvas using state.context.stroke().
- Canvas Initialization:
- The onMounted lifecycle hook is used to initialize the canvas.
- The canvas dimensions are set to match the window size.
- The 2D rendering context (state.context) is obtained from the canvas.
- Various properties related to line styling (color, join, cap, and width) are set.
This is the full code of the app for Drawing.vue.
<script setup>
import { ref, onMounted, reactive } from 'vue';
import PieSocket from "piesocket-js";
const canvas = ref(null);
const state = reactive({
drawing: false,
context: null,
lastX: 0,
lastY: 0
});
const startDrawing = (e) => {
state.drawing = true;
[state.lastX, state.lastY] = [e.offsetX, e.offsetY];
};
const draw = (e) => {
if (!state.drawing) return;
broadcastDraw(state.lastX, state.lastY, e.offsetX, e.offsetY);
drawLine(state.lastX, state.lastY, e.offsetX, e.offsetY);
[state.lastX, state.lastY] = [e.offsetX, e.offsetY];
};
const stopDrawing = () => {
state.drawing = false;
};
let broadcastDraw;
const pieSocket = new PieSocket({
clusterId: "s12117.nyc1",
apiKey: "0jlifmULE2eJFtCI2kjabsR7EdgaY4EWRZgdGMkE",
});
pieSocket.subscribe("drawing-room").then(ch => {
const channel = ch;
console.log("Channel is ready");
channel.listen("draw", (data) => {
const { x0, y0, x1, y1 } = JSON.parse(data);
drawLine(x0, y0, x1, y1);
});
broadcastDraw = (x0, y0, x1, y1) => {
channel.publish("draw", JSON.stringify({ x0, y0, x1, y1 }));
};
}).catch(error => {
console.error("Error subscribing to PieSocket channel:", error);
});
const drawLine = (x0, y0, x1, y1) => {
state.context.beginPath();
state.context.moveTo(x0, y0);
state.context.lineTo(x1, y1);
state.context.stroke();
};
onMounted(() => {
canvas.value.width = window.innerWidth;
canvas.value.height = window.innerHeight;
state.context = canvas.value.getContext('2d');
state.context.strokeStyle = "black";
state.context.lineJoin = 'round';
state.context.lineCap = 'round';
state.context.lineWidth = 10;
});
</script>
<template>
<div>
<canvas ref="canvas"
@mousedown="startDrawing"
@mousemove="draw"
@mouseup="stopDrawing"
@mouseout="stopDrawing"></canvas>
</div>
</template>
<style scoped>
canvas {
border: 5px solid black;
}
</style>
Add the following code in App.vue.
<template>
<div id="app">
<DrawingCanvas />
</div>
</template>
<script setup>
import DrawingCanvas from './components/DrawingCanvas.vue';
</script>
Run the Application
Now run the app with the given command.
npm run serve

Complete Code
The project is available on our GitHub : https://github.com/piehostHQ/ws-drawing-app
