Repository: bgstaal/multipleWindow3dScene
Branch: main
Commit: a99ecb8a865a
Files: 6
Total size: 11.3 KB
Directory structure:
gitextract_56hlv5ma/
├── .gitignore
├── README.md
├── WindowManager.js
├── index.html
├── main.js
└── three-LICENSE
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
node_modules
build
deploy
screens*
npm-debug.log
*.png
.env
.DS_Store
================================================
FILE: README.md
================================================
# Multiple Window 3D Scene using Three.js
## Introduction
This project demonstrates a unique approach to creating and managing a 3D scene across multiple browser windows using Three.js and localStorage. It's designed for developers interested in advanced web graphics and window management techniques.
## Features
- 3D scene creation and rendering with Three.js.
- Synchronization of 3D scenes across multiple browser windows.
- Dynamic window management and state synchronization using localStorage.
## Installation
Clone the repository and open `index.html` in your browser to start exploring the 3D scene.
```
git clone https://github.com/bgstaal/multipleWindow3dScene
```
## Usage
The main application logic is contained within `main.js` and `WindowManager.js`. The 3D scene is rendered in `index.html`, which serves as the entry point of the application.
## Structure and Components
- `index.html`: Entry point that sets up the HTML structure and includes the Three.js library and the main script.
- `WindowManager.js`: Core class managing window creation, synchronization, and state management across multiple windows.
- `main.js`: Contains the logic for initializing the 3D scene, handling window events, and rendering the scene.
- `three.r124.min.js`: Minified version of the Three.js library used for 3D graphics rendering.
## Detailed Functionality
- `WindowManager.js` handles the lifecycle of multiple browser windows, including creation, synchronization, and removal. It uses localStorage to maintain state across windows.
- `main.js` initializes the 3D scene using Three.js, manages the window's resize events, and updates the scene based on window interactions.
## Contributing
Contributions to enhance or expand the project are welcome. Feel free to fork the repository, make changes, and submit pull requests.
## License
This project is open-sourced under the MIT License.
## Acknowledgments
- The Three.js team for their comprehensive 3D library.
- x.com/didntdrinkwater for this readme.
## Contact
For more information and updates, follow [@_nonfigurativ_](https://twitter.com/_nonfigurativ_) on Twitter.
================================================
FILE: WindowManager.js
================================================
class WindowManager
{
#windows;
#count;
#id;
#winData;
#winShapeChangeCallback;
#winChangeCallback;
constructor ()
{
let that = this;
// event listener for when localStorage is changed from another window
addEventListener("storage", (event) =>
{
if (event.key == "windows")
{
let newWindows = JSON.parse(event.newValue);
let winChange = that.#didWindowsChange(that.#windows, newWindows);
that.#windows = newWindows;
if (winChange)
{
if (that.#winChangeCallback) that.#winChangeCallback();
}
}
});
// event listener for when current window is about to ble closed
window.addEventListener('beforeunload', function (e)
{
let index = that.getWindowIndexFromId(that.#id);
//remove this window from the list and update local storage
that.#windows.splice(index, 1);
that.updateWindowsLocalStorage();
});
}
// check if theres any changes to the window list
#didWindowsChange (pWins, nWins)
{
if (pWins.length != nWins.length)
{
return true;
}
else
{
let c = false;
for (let i = 0; i < pWins.length; i++)
{
if (pWins[i].id != nWins[i].id) c = true;
}
return c;
}
}
// initiate current window (add metadata for custom data to store with each window instance)
init (metaData)
{
this.#windows = JSON.parse(localStorage.getItem("windows")) || [];
this.#count= localStorage.getItem("count") || 0;
this.#count++;
this.#id = this.#count;
let shape = this.getWinShape();
this.#winData = {id: this.#id, shape: shape, metaData: metaData};
this.#windows.push(this.#winData);
localStorage.setItem("count", this.#count);
this.updateWindowsLocalStorage();
}
getWinShape ()
{
let shape = {x: window.screenLeft, y: window.screenTop, w: window.innerWidth, h: window.innerHeight};
return shape;
}
getWindowIndexFromId (id)
{
let index = -1;
for (let i = 0; i < this.#windows.length; i++)
{
if (this.#windows[i].id == id) index = i;
}
return index;
}
updateWindowsLocalStorage ()
{
localStorage.setItem("windows", JSON.stringify(this.#windows));
}
update ()
{
//console.log(step);
let winShape = this.getWinShape();
//console.log(winShape.x, winShape.y);
if (winShape.x != this.#winData.shape.x ||
winShape.y != this.#winData.shape.y ||
winShape.w != this.#winData.shape.w ||
winShape.h != this.#winData.shape.h)
{
this.#winData.shape = winShape;
let index = this.getWindowIndexFromId(this.#id);
this.#windows[index].shape = winShape;
//console.log(windows);
if (this.#winShapeChangeCallback) this.#winShapeChangeCallback();
this.updateWindowsLocalStorage();
}
}
setWinShapeChangeCallback (callback)
{
this.#winShapeChangeCallback = callback;
}
setWinChangeCallback (callback)
{
this.#winChangeCallback = callback;
}
getWindows ()
{
return this.#windows;
}
getThisWindowData ()
{
return this.#winData;
}
getThisWindowID ()
{
return this.#id;
}
}
export default WindowManager;
================================================
FILE: index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<title>3d example using three.js and multiple windows</title>
<script type="text/javascript" src="three.r124.min.js"></script>
<style type="text/css">
*
{
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<script type="module" src="main.js"></script>
</body>
</html>
================================================
FILE: main.js
================================================
import WindowManager from './WindowManager.js'
const t = THREE;
let camera, scene, renderer, world;
let near, far;
let pixR = window.devicePixelRatio ? window.devicePixelRatio : 1;
let cubes = [];
let sceneOffsetTarget = {x: 0, y: 0};
let sceneOffset = {x: 0, y: 0};
let today = new Date();
today.setHours(0);
today.setMinutes(0);
today.setSeconds(0);
today.setMilliseconds(0);
today = today.getTime();
let internalTime = getTime();
let windowManager;
let initialized = false;
// get time in seconds since beginning of the day (so that all windows use the same time)
function getTime ()
{
return (new Date().getTime() - today) / 1000.0;
}
if (new URLSearchParams(window.location.search).get("clear"))
{
localStorage.clear();
}
else
{
// this code is essential to circumvent that some browsers preload the content of some pages before you actually hit the url
document.addEventListener("visibilitychange", () =>
{
if (document.visibilityState != 'hidden' && !initialized)
{
init();
}
});
window.onload = () => {
if (document.visibilityState != 'hidden')
{
init();
}
};
function init ()
{
initialized = true;
// add a short timeout because window.offsetX reports wrong values before a short period
setTimeout(() => {
setupScene();
setupWindowManager();
resize();
updateWindowShape(false);
render();
window.addEventListener('resize', resize);
}, 500)
}
function setupScene ()
{
camera = new t.OrthographicCamera(0, 0, window.innerWidth, window.innerHeight, -10000, 10000);
camera.position.z = 2.5;
near = camera.position.z - .5;
far = camera.position.z + 0.5;
scene = new t.Scene();
scene.background = new t.Color(0.0);
scene.add( camera );
renderer = new t.WebGLRenderer({antialias: true, depthBuffer: true});
renderer.setPixelRatio(pixR);
world = new t.Object3D();
scene.add(world);
renderer.domElement.setAttribute("id", "scene");
document.body.appendChild( renderer.domElement );
}
function setupWindowManager ()
{
windowManager = new WindowManager();
windowManager.setWinShapeChangeCallback(updateWindowShape);
windowManager.setWinChangeCallback(windowsUpdated);
// here you can add your custom metadata to each windows instance
let metaData = {foo: "bar"};
// this will init the windowmanager and add this window to the centralised pool of windows
windowManager.init(metaData);
// call update windows initially (it will later be called by the win change callback)
windowsUpdated();
}
function windowsUpdated ()
{
updateNumberOfCubes();
}
function updateNumberOfCubes ()
{
let wins = windowManager.getWindows();
// remove all cubes
cubes.forEach((c) => {
world.remove(c);
})
cubes = [];
// add new cubes based on the current window setup
for (let i = 0; i < wins.length; i++)
{
let win = wins[i];
let c = new t.Color();
c.setHSL(i * .1, 1.0, .5);
let s = 100 + i * 50;
let cube = new t.Mesh(new t.BoxGeometry(s, s, s), new t.MeshBasicMaterial({color: c , wireframe: true}));
cube.position.x = win.shape.x + (win.shape.w * .5);
cube.position.y = win.shape.y + (win.shape.h * .5);
world.add(cube);
cubes.push(cube);
}
}
function updateWindowShape (easing = true)
{
// storing the actual offset in a proxy that we update against in the render function
sceneOffsetTarget = {x: -window.screenX, y: -window.screenY};
if (!easing) sceneOffset = sceneOffsetTarget;
}
function render ()
{
let t = getTime();
windowManager.update();
// calculate the new position based on the delta between current offset and new offset times a falloff value (to create the nice smoothing effect)
let falloff = .05;
sceneOffset.x = sceneOffset.x + ((sceneOffsetTarget.x - sceneOffset.x) * falloff);
sceneOffset.y = sceneOffset.y + ((sceneOffsetTarget.y - sceneOffset.y) * falloff);
// set the world position to the offset
world.position.x = sceneOffset.x;
world.position.y = sceneOffset.y;
let wins = windowManager.getWindows();
// loop through all our cubes and update their positions based on current window positions
for (let i = 0; i < cubes.length; i++)
{
let cube = cubes[i];
let win = wins[i];
let _t = t;// + i * .2;
let posTarget = {x: win.shape.x + (win.shape.w * .5), y: win.shape.y + (win.shape.h * .5)}
cube.position.x = cube.position.x + (posTarget.x - cube.position.x) * falloff;
cube.position.y = cube.position.y + (posTarget.y - cube.position.y) * falloff;
cube.rotation.x = _t * .5;
cube.rotation.y = _t * .3;
};
renderer.render(scene, camera);
requestAnimationFrame(render);
}
// resize the renderer to fit the window size
function resize ()
{
let width = window.innerWidth;
let height = window.innerHeight
camera = new t.OrthographicCamera(0, width, 0, height, -10000, 10000);
camera.updateProjectionMatrix();
renderer.setSize( width, height );
}
}
================================================
FILE: three-LICENSE
================================================
The MIT License
Copyright © 2010-2023 three.js authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
gitextract_56hlv5ma/ ├── .gitignore ├── README.md ├── WindowManager.js ├── index.html ├── main.js └── three-LICENSE
SYMBOL INDEX (22 symbols across 2 files)
FILE: WindowManager.js
class WindowManager (line 1) | class WindowManager
method constructor (line 10) | constructor ()
method #didWindowsChange (line 43) | #didWindowsChange (pWins, nWins)
method init (line 63) | init (metaData)
method getWinShape (line 78) | getWinShape ()
method getWindowIndexFromId (line 84) | getWindowIndexFromId (id)
method updateWindowsLocalStorage (line 96) | updateWindowsLocalStorage ()
method update (line 101) | update ()
method setWinShapeChangeCallback (line 125) | setWinShapeChangeCallback (callback)
method setWinChangeCallback (line 130) | setWinChangeCallback (callback)
method getWindows (line 135) | getWindows ()
method getThisWindowData (line 140) | getThisWindowData ()
method getThisWindowID (line 145) | getThisWindowID ()
FILE: main.js
function getTime (line 25) | function getTime ()
function init (line 53) | function init ()
function setupScene (line 68) | function setupScene ()
function setupWindowManager (line 90) | function setupWindowManager ()
function windowsUpdated (line 106) | function windowsUpdated ()
function updateNumberOfCubes (line 111) | function updateNumberOfCubes ()
function updateWindowShape (line 140) | function updateWindowShape (easing = true)
function render (line 148) | function render ()
function resize (line 188) | function resize ()
Condensed preview — 6 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (13K chars).
[
{
"path": ".gitignore",
"chars": 70,
"preview": "node_modules\nbuild\ndeploy\nscreens*\nnpm-debug.log\n*.png\n.env\n.DS_Store\n"
},
{
"path": "README.md",
"chars": 2134,
"preview": "# Multiple Window 3D Scene using Three.js\n\n## Introduction\nThis project demonstrates a unique approach to creating and m"
},
{
"path": "WindowManager.js",
"chars": 3014,
"preview": "class WindowManager \n{\n\t#windows;\n\t#count;\n\t#id;\n\t#winData;\n\t#winShapeChangeCallback;\n\t#winChangeCallback;\n\t\n\tconstructo"
},
{
"path": "index.html",
"chars": 328,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n\t<title>3d example using three.js and multiple windows</title>\n\t<script type=\"te"
},
{
"path": "main.js",
"chars": 4946,
"preview": "import WindowManager from './WindowManager.js'\n\n\n\nconst t = THREE;\nlet camera, scene, renderer, world;\nlet near, far;\nle"
},
{
"path": "three-LICENSE",
"chars": 1079,
"preview": "The MIT License\n\nCopyright © 2010-2023 three.js authors\n\nPermission is hereby granted, free of charge, to any person obt"
}
]
About this extraction
This page contains the full source code of the bgstaal/multipleWindow3dScene GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 6 files (11.3 KB), approximately 3.2k tokens, and a symbol index with 22 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.