Sorcherer is a lightweight JavaScript library for attaching dynamic HTML overlays to Three.js Object3D instances. With Sorcherer, you can define overlays in your HTML using a custom <realm>
tag and have them automatically update (with culling, distance‑based scaling, rotation via CSS, auto‑centering, dynamic variables, and more) based on your 3D scene’s state.
- HTML Overlays for 3D Objects: Attach DOM overlays to any Three.js Object3D.
-
Custom
<realm>
Tag: Easily declare overlay elements with custom attributes. - Distance-Based Scaling: Scale overlays based on the actual Euclidean distance from the camera.
-
Rotation Simulation: Apply rotation from your Object3D via CSS’s
rotate
property. [!Currently Broken : D!] - Auto-Centering: Optionally center overlays relative to their computed screen positions.
- DOM Culling: Efficient frustum culling to update only visible overlays.
-
Dynamic Variables: Use placeholders in overlay content (e.g.
$dogCount=20$
) that become accessible as properties on the overlay instance. -
Global Access & Cloning: Access all overlays via
Sorcherer.instancesById
and clone overlays easily using theattachClone
method.
Install via npm:
npm install sorcherer
Or clone the repository directly from GitHub.
Use Sorcherer as an ES module:
import { Sorcherer } from 'sorcherer';
Define overlays in your HTML with a custom <realm>
tag. Each child element must have an idm
attribute (matching a registered Object3D's name) and can include additional attributes:
-
simulate3D="true"
– Enable distance‑based scaling. -
simulateRotation="true"
– Enable rotation via the CSSrotate
property. -
offset="x,y,z"
– Set a custom offset (comma‑separated values). -
autoCenter="true"
– Center the overlay relative to its computed screen position. -
scaleMultiplier="1.5"
– Multiply the computed scale factor (default is given bySorcherer.defaultScaleMultiplier
).
Overlay content may include dynamic variable placeholders in the form $varName$
or $varName=defaultValue$
.
Below is a complete HTML example that creates a Three.js scene with a rotating cube, registers the cube with Sorcherer, and defines an overlay via <realm>
that displays osciliating_cat.gif. It also demonstrates dynamic variables and global instance access.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Sorcherer Demo – Osciliating Cat</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #222;
}
</style>
</head>
<body>
<!-- Define overlays using the custom <realm> tag -->
<realm>
<!--
Attributes:
idm="cube" => Matches a registered Object3D with name "cube"
simulate3D="true" => Enable distance-based scaling
simulateRotation="true" => Enable rotation via the CSS "rotate" property
offset="0,0.5,0" => Custom vertical offset
autoCenter="true" => Center the overlay relative to its computed position
scaleMultiplier="1.5" => Multiply the computed scale factor by 1.5
-->
<div idm="cube" simulate3D="true" simulateRotation="true" offset="0,0.5,0" autoCenter="true" scaleMultiplier="1.5">
<img src="osciliating_cat.gif" alt="Osciliating Cat" style="max-width:100px; max-height:100px;"><br>
Dog count: $dogCount=20$
</div>
</realm>
<script type="module">
import * as THREE from 'three';
import { Sorcherer } from 'sorcherer';
// Set up the Three.js scene, camera, and renderer.
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Create a cube, name it "cube", and add it to the scene.
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });
const cube = new THREE.Mesh(geometry, material);
cube.name = "cube";
scene.add(cube);
// Register the cube with Sorcherer.
Sorcherer.registerObject3D(cube);
Sorcherer.attachFromRealm();
Sorcherer.autoSetup(camera, renderer);
// Demonstrate updating a dynamic variable after 3 seconds.
setTimeout(() => {
Sorcherer.instancesById["cube"].dogCount = "42";
}, 3000);
// Animate the cube: circular motion in the xz-plane and oscillation along the y-axis.
function animate() {
requestAnimationFrame(animate);
const t = Date.now() * 0.001;
cube.position.x = Math.cos(t) * 2;
cube.position.z = Math.sin(t) * 2;
cube.position.y = Math.sin(t * 2) * 1;
cube.rotation.x += 0.005;
cube.rotation.y += 0.005;
cube.rotation.z += 0.01;
renderer.render(scene, camera);
}
animate();
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
</body>
</html>
-
Sorcherer.registerObject3D(object)
Registers a Three.js Object3D with the library using itsname
property as the key. -
Sorcherer.attachFromRealm()
Scans the DOM for a<realm>
tag and attaches overlays based on child elements’ custom attributes. -
Sorcherer.autoSetup(camera, renderer, interval?)
Starts the auto-update loop that refreshes overlays based on camera changes. The optionalinterval
(in ms) defaults to 16. -
Sorcherer.defaultScaleMultiplier
A static property that defines the default multiplier for distance‑based scaling. You can adjust it as needed:Sorcherer.defaultScaleMultiplier = 1.2;
-
Sorcherer.instancesById
A global object mapping Object3D names (idm) to their overlay instances. You can access overlays directly, e.g.:Sorcherer.instancesById["cube"].dogCount = "42";
-
attach(innerHTML)
Attaches the provided HTML content to the overlay element (usinginnerHTML
so that markup is parsed). This method also parses dynamic variable placeholders (e.g.$dogCount=20$
) and creates getters/setters for direct access. -
bufferInstance(camera, renderer)
Updates the overlay’s position, scaling (distance‑based), rotation (via CSSrotate
), and auto-centering based on the current camera and Object3D state. It also re-renders dynamic variables. -
dispose()
Removes the overlay element and cleans up references. -
attachClone(targetObject, newName)
Clones the current overlay instance and attaches the clone to the specifiedtargetObject
. The new overlay is assigned the providednewName
and stored inSorcherer.instancesById
under that name. -
Dynamic Variable Access:
After callingattach()
, any dynamic variable placeholder in the template becomes accessible directly as a property of the overlay instance. For example, if the template contains$dogCount=20$
:Sorcherer.instancesById["cube"].dogCount = "42"; console.log(Sorcherer.instancesById["cube"].dogCount); // "42"
Contributions are welcome! Please open issues or submit pull requests on the GitHub repository.
This project is licensed under the MIT License.