💪 😵💫 😖 💪 💪 🏃♂️ 😖 🌀 🏃 💪
Was meint Multi-Window Experiences?
Wohin geht es?
Welche Multi-Window-Cases könnten betrachtet werden?
Position on Screen
Multiple Windows
Distance
Angle
Right or left
Intersection
Overlap & Casting
Multi Screen
Woher kommen die Daten
und wie können wir sie austauschen?
Position on screen via window.screenX & window.screenY
Share Data via window.opener
Share via Broadcast Channel API
Store shared data via Local Storage
Use of a higher-level Shared Worker
Using an external component: WebSocket
No event listener is available 😢
Position on screen via window.screenX & window.screenY
.Step 1: Detect Window Position
console.log('Window position:', window.screenX, window.screenY);
. Position on screen via window.screenX & window.screenYStep 2: Observe Window Position
const myWindowPosition = {
x: false,
y: false
}
/* break */
const trackMyWindowPosition = () => {
if (myWindowPosition.x === window.screenX
&& myWindowPosition.y === window.screenY) return;
/* break */
myWindowPosition.x = window.screenX;
myWindowPosition.y = window.screenY;
/* break */
// Do something meaningful with the data 🕺🏼
console.log(myWindowPosition.x, myWindowPosition.y);
}
/* break */
trackMyWindowPosition();
const observeMyWindowPosition = setInterval(trackMyWindowPosition, 500);
Baseline Widely available
Share Data via window.opener
Baseline Widely available Share Data via window.openerUsing window.opener
// In opener/parent window
/* break */
const childWindowPosition = {
x: false,
y: false
}
/* break */
// Open child window
childWindow = window.open(url, 'child-window');
/* break */
// Read window position from child
if (childWindowPosition.x === childWindow.screenX
&& childWindowPosition.y === childWindow.screenY) return;
/* break */
childWindowPosition.x = childWindow.screenX;
childWindowPosition.y = childWindow.screenY;
// In opened/child window
/* break */
const parentWindowPosition = {
x: false,
y: false
}
/* break */
// Read window position from parent
if (parentWindowPosition.x === window.opener.screenX
&& parentWindowPosition.y === window.opener.screenY) return;
/* break */
parentWindowPosition.x = window.opener.screenX;
parentWindowPosition.y = window.opener.screenY;
Advantages of window.opener 🥳
Disadvantages 😢
Publish–Subscribe Messaging
Share Data via Broadcast Channel API & window.postMessage
The window.postMessage() method safely enables cross-origin communication between Window objects; e.g., between a page and a pop-up that it spawned, or between a page and an iframe embedded within it. The window.postMessage() method safely enables cross-origin communication between Window objects; e.g., between a page and a pop-up that it spawned, or between a page and an iframe embedded within it. The Broadcast Channel API is restricted on same origin communication. If a participant (tab, window, worker) sends a message via BroadcastChannel.postMessage(), it is sent to all other participants in the channel - but not to the sender itself. ⚠️ Note that window.postMessage() also works without the Broadcast Channel API. Share Data via Broadcast Channel API & window.postMessageUsing Broadcast Channel API
// In (first) window
/* break */
// Open Broadcast Channel
const windowPositionChannel = new BroadcastChannel('window-position');
/* break */
/* some lines of funky code */
/* break */
// Send my window position to the channel
windowPositionChannel.postMessage(myWindowPosition);
/* break */
// Receive messages from the channel
windowPositionChannel.onmessage = (event) => {
const otherWindowPosition = event.data;
/* break */
// Do something meaningful with the data 🕺🏼
console.log(otherWindowPosition.x, otherWindowPosition.y);
}
// In (second) window
/* break */
// Open Broadcast Channel
const windowPositionChannel = new BroadcastChannel('window-position');
/* break */
/* some lines of funky code */
/* break */
// Send my window position to the channel
windowPositionChannel.postMessage(myWindowPosition);
/* break */
// Receive messages from the channel
windowPositionChannel.onmessage = (event) => {
const otherWindowPosition = event.data;
/* break */
// Do something meaningful with the data 🕺🏼
console.log(otherWindowPosition.x, otherWindowPosition.y);
}
Broadcast Channel API & window.postMessage()
Advantages 🥳
Disadvantages 😢
Storage Event Triggering Paradigm
Store shared data via Local Storage
LocalStorage allows browser windows of the same origin to persistently store and share string-based key–value pairs. The storage event lets other open windows react to changes in real time, making it useful for simple cross-window communication. Store shared data via Local StorageStore shared data via LocalStorage
// Write changes to local storage
const trackMyWindowPosition = () => {
/* break */
if (myWindowPosition.x === window.screenX
&& myWindowPosition.y === window.screenY) return;
/* break */
myWindowPosition.x = window.screenX;
myWindowPosition.y = window.screenY;
/* break */
// Store Window Data in Local Storage
localStorage.setItem('windowPosition', JSON.stringify(myWindowPosition));
}
// Listen to changes in local storage
window.addEventListener('storage', (event) => {
/* break */
if(!event.key === 'windowPosition') return;
const otherWindowPosition = JSON.parse(event.newValue);
/* break */
// Do something meaningful with the data 🕺🏼
console.log(otherWindowPosition.x, otherWindowPosition.y);
});
Advantages 🥳
Disadvantages 😢
Shared Process with Message-Based Communication
Use of a higher-level Shared Worker
This creates a new connection to an existing or newly started SharedWorker whose code is located in the shared.js file. In contrast to workers, which start a separate thread per window or tab, a SharedWorker is only started once per Origin - all tabs share the same worker process. Use of a higher-level Shared WorkerShared Worker with postMessage()
// Shared Worker Code in shared.js
/* break */
const connections = [];
/* break */
onconnect = function (e) {
const port = e.ports[0];
connections.push(port);
/* break */
port.onmessage = function (event) {
const data = event.data;
/* break */
// Echo to all other windows
connections.forEach(p => {
if (p !== port) {
p.postMessage(data);
}
});
};
/* break */
port.start(); // Required for older browsers
};
// Client Code
// Start or connect to worker
const worker = new SharedWorker('shared.js');
worker.port.start();
/* break */
const trackMyWindowPosition = () => {
/* ... */
// Store Window Data in Shared Worker
worker.port.postMessage(myWindowPosition);
}
/* break */
// Observe my window position
const observeMyWindowPosition = setInterval(trackMyWindowPosition, 500);
/* break */
// Receive messages from the worker
worker.port.onmessage = (event) => {
/* break */
const otherWindowPosition = event.data;
/* break */
// Do something meaningful with the data 🕺🏼
console.log(otherWindowPosition.x, otherWindowPosition.y);
};
Advantages 🥳
Disadvantages 😢
01. Share Data between Windows: You are given a starter file with a code skeleton. Your job is to implement a working communication channel that allows each window to display: its own position (based on window.screenX / screenY) and the position of the other window. 30min
This SharedWorker script manages real-time communication between multiple browser windows by tracking and sharing their screen positions.
Each connected window can send its current position, retrieve its own stored position, or request the positions of all other connected windows.
p5.js is an open-source JavaScript library that brings the core principles of creative coding to the web. It was initiated in 2013 by Lauren McCarthy, an artist and programmer, as a browser-based reinterpretation of Processing, the influential visual programming environment developed by Casey Reas and Ben Fry at the MIT Media Lab.
The tools uses a simple sketch structure based on two main functions: setup(), which runs once at the beginning, and draw(), which runs continuously like a loop. To get started, you can write your code in the p5.js Web Editor.Core Structure of a Sketch
function setup() {
createCanvas(400, 400);
}
/* break */
function draw() {
background(220);
ellipse(mouseX, mouseY, 50, 50);
}
colorMode() in p5.js sets how colors are interpreted. Using HSB (Hue, Saturation, Brightness) is often better because it separates color, intensity, and lightness, making color control more intuitive.Useful Snippets: ColorMode
function setup() {
createCanvas(400, 400);
colorMode(HSB, 360, 100, 100, 100);
fill(90, 100, 70, 100);
stroke(270, 100, 70, 100);
strokeWeight(10);
}
function draw() {
background(0, 0, 0, 100);
ellipse(mouseX, mouseY, 50, 50);
}
random() in p5.js returns a random number, useful for adding unpredictability. randomSeed() sets the starting point for random values, making results repeatable — helpful for debugging or creating consistent outcomes.Useful Snippets: random() & randomSeed()
function setup() {
createCanvas(400, 400);
colorMode(HSB, 360, 100, 100, 100);
fill(90, 100, 70, 100);
stroke(270, 100, 70, 100);
strokeWeight(10);
}
function draw() {
background(0, 0, 0, 100);
const dotSize = random(-20, 100);
ellipse(mouseX, mouseY, dotSize);
}
map() in p5.js converts a number from one range to another. It's useful for scaling values, like turning mouse position into color or speed.Useful Snippets: map()
function setup() {
createCanvas(400, 400);
colorMode(HSB, 360, 100, 100, 100);
fill(90, 100, 70, 100);
}
function draw() {
background(0, 0, 0, 100);
const hue = map(mouseX, 0, 400, 0, 360);
const dotSize = map(mouseY, 0, 400, 0, 50);
fill(hue, 100, 100, 100);
ellipse(mouseX, mouseY, dotSize);
}
dist() in p5.js calculates the distance between two points. It's useful for measuring how far apart objects are, often used in movement or collision detection.Useful Snippets: dist()
function setup() {
createCanvas(400, 400);
colorMode(HSB, 360, 100, 100, 100);
fill(90, 100, 70, 100);
}
function draw() {
background(0, 0, 0, 100);
const dotSize = dist(0, 0, mouseX, mouseY);
ellipse(width/2, height/2, dotSize);
}
translate() in p5.js moves the origin of the drawing space, letting you draw shapes in a new position. rotate() turns the drawing space, useful for rotating shapes around a point.Brain-Freeze: translate() & rotate()
function setup() {
createCanvas(400, 400);
colorMode(HSB, 360, 100, 100, 100);
fill(335, 92, 87, 100);
}
function draw() {
background(0, 0, 0, 100);
translate(width/2, height/2);
rotate(45);
rect(-30, -30, 60, 60);
}
This code creates a rotating rectangle in the center of the canvas. The translate() function moves the origin to the center, and rotate(angle) spins the rectangle around that point. angle increases over time, causing continuous rotation, while the semi-transparent background creates a fading trail effect.Useful Snippets: Simple Animation
let angle = 0;
function setup() {
createCanvas(400, 400);
colorMode(HSB, 360, 100, 100, 100);
fill(335, 92, 87, 100);
}
function draw() {
background(0, 0, 0, 10);
translate(width/2, height/2);
rotate(angle);
rect(0, 0, 60, 60);
angle += 0.08;
}
Share Window Data Concepts & p5.js
In order to create a link between the sketch and the screen, we move and scale the canvas to the size of the screen.
In order to create a link between the sketch and the screen, we move and scale the canvas to the size of the screen.
This code adjusts the canvas based on the browser window’s position on the screen, allowing the sketch to stay aligned with the physical screen space.Fit Canvas to Screen Size
let posX, posY;
function draw() {
background(0, 0, 0, 100);
if (drawingParams.context === 'screen') {
// Move the origin of the canvas to the top left corner of the screen
const originX = -window.screenX;
const originY = -window.screenY;
canvas.width = screen.width;
canvas.height = screen.height;
translate(originX, originY);
posX = width / 2;
posY = height / 2;
}else{
posX = window.innerWidth / 2;
posY = window.innerHeight / 2;
}
ellipse(posX, posY, 60, 60);
}
02. Draw Line between Windows: Extend the p5 sketch so that a line is drawn from your own center to the other window’s center. 15min
03. Dual-Window Visualization Experiment Turn a given starter sketch into an interactive or generative visual system that lives across two windows. 30min
How could the transfer of an element from one window to another be realized?
04. Visual Handoff Between Windows: You're given a starter sketch with a simple visual and a working SharedWorker; extend it so that when two windows visibly overlap on screen, the visual automatically moves to the other window. 30min
There are many technical ways to exchange data between browser windows — from direct messaging with postMessage to reactive broadcasting with BroadcastChannel, or even shared background logic via SharedWorker.
Most approaches ultimately rely on sending messages back and forth, but the real challenge lies in crafting a meaningful design concept around that connection.
In the end, your architecture is just the foundation. What you build on top is what matters most.
By the way, designer and coder Björn Staal works with the localStorage-based approach — showing that even the simplest mechanisms can lead to compelling, interactive experiences if paired with a strong idea.
Danke für's Mitmachen