<head>
<meta charset="utf-8" />
<title>Shared Whiteboard Powered by Amazon IVS</title>
</head>
<body style="padding: 10px;">
<div class="card mb-3" id="settings">
<div class="card-header bg-dark text-white">
Settings
</div>
<div class="card-body">
<div class="mb-3 row">
<label for="chat-userid" class="col-sm-2 col-form-label">Chat UserId</label>
<div class="col-sm-10">
<input type="text" class="col-sm-10 form-control" id="chat-userid" required />
</div>
</div>
<div class="mb-3 row">
<label for="pen-color" class="col-sm-2 col-form-label">Pen Color</label>
<div class="col-sm-10">
<input type="color" class="form-control form-control-color" id="pen-color" required />
</div>
</div>
<div class="mb-3 row">
<label for="chat-token" class="col-sm-2 col-form-label">Chat Token</label>
<div class="col-sm-10">
<input type="text" class="col-sm-10 form-control" id="chat-token" required />
</div>
</div>
<div class="mb-3 row">
<label for="chat-endpoint" class="col-sm-2 col-form-label">Endpoint</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="chat-endpoint" placeholder="Begins with: wss://" value="wss://edge.ivschat.us-east-1.amazonaws.com" required />
</div>
</div>
<div class="mb-3 row">
<div class="col-sm-10 offset-sm-2">
<button type="submit" class="btn btn-dark" id="submit-settings">Submit</button>
</div>
</div>
</div>
</div>
<div id="whiteboard-container" class="w-100 vh-100">
<canvas id="whiteboard" class="w-100 h-100 border rounded border-dark shadow">
Sorry, your browser does not support HTML5 canvas technology.
</canvas>
</div>
</body>
</html>
#whiteboard {
cursor: crosshair;
}
window.isDrawing = false;
window.chatEndpoint;
window.chatToken;
window.userId;
window.penColor;
window.connection;
window.throttlePause;
window.queue = [];
window.maxQueueSize = 20;
const throttle = (callback, time) => {
if (window.throttlePause) return;
window.throttlePause = true;
setTimeout(() => {
callback();
window.throttlePause = false;
}, time);
};
const handleQueue = (event) => {
if(window.queue.length <= window.maxQueueSize) {
window.queue.push(event);
}
if(window.queue.length === window.maxQueueSize || event.type == 'mouseup') {
sendEvents();
}
};
const sendEvents = () => {
const payload = {
'Action': 'SEND_MESSAGE',
'Content': '[whiteboard event]',
'Attributes': {
'type': 'whiteboard',
'color': window.penColor,
'events': JSON.stringify(window.queue),
}
}
try {
window.connection.send(JSON.stringify(payload));
window.queue = [];
}
catch(e) {
console.error(e);
}
}
const onMouseDown = (e) => {
const canvasEl = document.getElementById('whiteboard');
const ctx = canvasEl.getContext('2d');
ctx.beginPath();
const x = e.x;
const y = e.y;
ctx.moveTo(x, y);
};
const onMouseMove = (e, color) => {
const canvasEl = document.getElementById('whiteboard');
const ctx = canvasEl.getContext('2d');
const x = e.x;
const y = e.y;
ctx.lineTo(x, y);
ctx.strokeStyle = color || window.penColor;
ctx.stroke();
};
const onMouseUp = (e) => {
const canvasEl = document.getElementById('whiteboard');
const ctx = canvasEl.getContext('2d');
// any necessary actions
// such as:
// ctx.closePath();
}
const init = () => {
window.chatEndpoint = document.getElementById('chat-endpoint').value;
window.userId = document.getElementById('chat-userid').value;
window.chatToken = document.getElementById('chat-token').value;
window.penColor = document.getElementById('pen-color').value;
if(!window.chatEndpoint || !window.chatToken || !window.userId) {
alert('Chat Endpoint, Token and UserId are required!');
return;
}
document.getElementById('settings').classList.add('d-none')
window.connection = new WebSocket(window.chatEndpoint, window.chatToken);
window.connection.addEventListener('message', (e) => {
const data = JSON.parse(e.data);
const msgType = data.Attributes.type;
if(msgType == 'whiteboard') {
const events = JSON.parse(data.Attributes.events);
const color = data.Attributes.color;
const eventUserId = data.Sender.UserId;
events.forEach(e => {
const type = e.type;
if(eventUserId != window.userId) {
switch(type){
case 'mousedown':
onMouseDown({x: e.x, y: e.y});
break;
case 'mousemove':
onMouseMove({x: e.x, y: e.y}, color);
break;
case 'mouseup':
onMouseUp({});
break;
};
}
});
}
// otherwise, handle as an incoming chat...
});
const whiteboardContainer = document.getElementById('whiteboard-container');
const canvasEl = document.getElementById('whiteboard');
canvasEl.width = whiteboardContainer.offsetWidth;
canvasEl.height = whiteboardContainer.offsetHeight;
const ctx = canvasEl.getContext('2d');
ctx.lineWidth = 5;
ctx.fillStyle='#fff';
ctx.fillRect(0, 0, canvasEl.width, canvasEl.height);
if(canvasEl){
canvasEl.addEventListener('mousedown', (e) => {
window.isDrawing = true;
const evt = {x: e.offsetX, y: e.offsetY, type: 'mousedown'};
onMouseDown(evt);
handleQueue(evt);
});
canvasEl.addEventListener('mousemove', (e) => {
if(window.isDrawing) {
const evt = {x: e.offsetX, y: e.offsetY, type: 'mousemove'};
throttle(() => {
handleQueue(evt);
}, 50);
onMouseMove(evt);
}
});
canvasEl.addEventListener('mouseup', (e) => {
window.isDrawing = false;
onMouseUp({});
handleQueue({type: 'mouseup'});
});
}
};
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('pen-color').value = `#${Math.floor(Math.random()*16777215).toString(16)}`;
document.getElementById('submit-settings').addEventListener('click', () => {
init();
})
});
This Pen doesn't use any external JavaScript resources.