Cross‑Window Real‑Time Rendering with Canvas, window.screenX/Y and localStorage
This article demonstrates how to create a multi‑window, real‑time interactive animation using pure JavaScript by drawing circles on a canvas, sharing each window's screen coordinates and colors through localStorage, and connecting them with lines across browsers.
The author describes a technique for building a cross‑window, real‑time interactive animation entirely with JavaScript, without third‑party libraries. Each browser window draws a circle at its center on a canvas , stores its screen coordinates ( window.screenX , window.screenY ) and a color in localStorage , and reads other windows' data to render connecting lines.
Key APIs used :
window.requestAnimationFrame – for the animation loop.
const { screenX, screenY } = window – to obtain the window's position relative to the screen.
const barHeight = window.outerHeight - window.innerHeight – to calculate the browser UI height.
canvas – the drawing surface.
localStorage – shared storage for all pages under the same origin.
Core drawing logic (simplified) :
const alert1 = document.getElementById('alert1');
const alert2 = document.getElementById('alert2');
const canvas1 = document.getElementById('canvas1');
const ctx = canvas1.getContext('2d');
function draw() {
const { clientWidth, clientHeight } = document.body;
const { screenX, screenY } = window;
const barHeight = window.outerHeight - window.innerHeight;
// log current window info
alert1.textContent = JSON.stringify({ clientWidth, clientHeight, screenX, screenY, barHeight }, null, 2);
canvas1.width = clientWidth;
canvas1.height = clientHeight;
const x = clientWidth / 2;
const y = clientHeight / 2;
// draw own circle
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(x, y, 15, 0, Math.PI * 2);
ctx.fill();
const position = { top: y + barHeight + screenY, left: x + screenX, color };
// draw other windows' circles and connecting lines
getOtherKeys().forEach(k => {
const position2 = JSON.parse(localStorage.getItem(k));
const w = position2.left - position.left;
const h = position2.top - position.top;
ctx.fillStyle = position2.color;
ctx.beginPath();
ctx.arc(x + w, y + h, 15, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + w, y + h);
ctx.stroke();
});
// update own position in storage
localStorage.setItem(key, JSON.stringify(position));
alert2.textContent = JSON.stringify(localStorage, null, 2);
window.requestAnimationFrame(draw);
}
window.requestAnimationFrame(draw);Utility functions manage storage keys, assign a unique key to each window, and clean up on window.onunload :
function getOtherKeys() {
const keys = [];
for (let i = 0; i < localStorage.length; i++) {
const k = Number(localStorage.key(i));
if (!isNaN(k)) keys.push(k);
}
return keys.sort((a, b) => a - b);
}
window.onunload = function () {
localStorage.removeItem(key);
};The article concludes with the full source code, a summary of current limitations (e.g., missing connection lines when windows overlap), and links to the GitHub repository and live demo for further exploration.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
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.