npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

scene-bridge-3d

v1.0.5

Published

A Three.js Scene extension to sync 3D objects with DOM elements.

Readme

Scene Bridge 3D

Scene-Bridge-3D-Logo

Sync real DOM layout → live Three.js objects. Pixel mapping, scaling, anchoring, depth & rotation — all driven by the HTML/CSS you already write.

scene-bridge-3d is a lightweight extension of THREE.Scene that lets you attach any Object3D to a DOM element and keep it perfectly aligned in 3D space. It converts DOM position & size into world coordinates, supports multiple projection/target modes, anchors, responsive rescaling, optional depth & rotation via attributes, and even raycasting to arbitrary geometry.


🚀 Why Use This?

  • Zero manual math for pixel → world conversions
  • Drive 3D transforms directly from CSS layout or animated attributes
  • Works with resizes, DPR changes (optional), and dynamic DOM movement
  • Flexible projection targets: object depth or raycast hits
  • Extend/opt-out per object with granular options

✨ Features

  • 🔗 Attach any THREE.Object3D to a DOM element: scene.add(mesh, element[, options])
  • 🎯 Anchor control: center, top-left, or custom [ax, ay] (0–1)
  • 🎥 Projection target modes: objectZ or raycast
  • 🧲 Raycast mode: project DOM point onto arbitrary meshes (e.g. terrain / model surface)
  • 📐 Automatic scale mapping: element width/height → object scale (non‑uniform supported)
  • 🧭 Optional depth & rotation via data attributes (data-z, data-rot-x|y|z)
  • 🧬 Maintains geometry center offset (avoids visual drift)
  • 🪄 Dynamic reassignment: scene.setTracking(obj, newElement) or detach with null
  • 🛡️ preserveZ lets you keep depth while syncing only X/Y
  • 🖥️ Optional DPR-aware scaling (applyDPR)
  • 🪶 Minimal overhead: only updates tracked objects
  • 🧪 TypeScript friendly — full type exports

📦 Installation

Peer dependency: three@^0.160.0

npm install three scene-bridge-3d
# or
yarn add three scene-bridge-3d
# or
pnpm add three scene-bridge-3d

ESM / TypeScript:

import * as THREE from 'three';
import Scene from 'scene-bridge-3d';

CommonJS:

const THREE = require('three');
const Scene = require('scene-bridge-3d').default;

⚡ Quick Start

import * as THREE from 'three';
import Scene from 'scene-bridge-3d';

const camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 0.1, 100);
camera.position.set(0, 0, 5);

const scene = new Scene(camera);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

const geo = new THREE.BoxGeometry(1, 1, 1);
const mat = new THREE.MeshNormalMaterial();
const mesh = new THREE.Mesh(geo, mat);

const el = document.querySelector('#card'); // some absolutely or relatively positioned element
scene.add(mesh, el, { anchor: 'center' });

function loop(t?: number) {
  requestAnimationFrame(loop);
  scene.update();
  renderer.render(scene, camera);
}
loop();

window.addEventListener('resize', () => {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(innerWidth, innerHeight);
});

HTML:

<div id="card" style="position:absolute;left:40vw;top:30vh;width:160px;height:200px;">
  Hover me
</div>

Animate with CSS or GSAP — the 3D mesh follows automatically.


🛠 Advanced Usage

Data Attribute Augmentation

<div id="panel" data-z="0.5" data-rot-x="30" data-rot-y="-20" data-rot-z="15"></div>
  • data-z: depth offset (scaled to world units)
  • data-rot-*: Euler rotations in degrees

Reassign / Detach Tracking

scene.setTracking(mesh, document.querySelector('#other'));
scene.setTracking(mesh, null); // stop syncing

Preserve Existing Z

scene.add(mesh, el, { preserveZ: true }); // only x/y & scale sync
mesh.position.z = 2; // you control depth manually

Custom Anchor

scene.add(mesh, el, { anchor: [0, 1] }); // left-bottom corner of element

Target Modes

// (default) keep object's current Z plane
scene.add(mesh, el, { target: 'objectZ' });

// Raycast to arbitrary objects (first hit wins)
scene.add(mesh, el, { target: 'raycast', raycastObjects: [terrain, model] });

Raycast Projection Example

scene.add(markerMesh, domPointEl, {
  target: 'raycast',
  raycastObjects: [terrainMesh],
  anchor: 'center'
});

The DOM element's screen position is projected as a ray; the first intersection point becomes the mesh position.

DPR-Aware Scaling

scene.add(mesh, el, { applyDPR: true });

Useful when using high pixel density canvases.


🔍 API Reference

class Scene extends THREE.Scene

Constructor

new Scene(camera: THREE.PerspectiveCamera);

Stores a reference to camera for projections.

Overloaded add

scene.add(object: THREE.Object3D, domElement: Element, options?: DomObjectOptions): Scene;
scene.add(...objects: THREE.Object3D[]): Scene; // native passthrough

Associates a DOM element with the object (first signature) or behaves like the normal Three.js Scene.add (second signature).

setTracking(object, el, options?)

scene.setTracking(obj, element);      // start or swap
scene.setTracking(obj, null);         // detach
scene.setTracking(obj, element, { anchor: 'top-left' });

Adds tracking if not tracked; swapping element updates mapping. Passing null removes from internal sync list.

update(dt?)

Call each frame (typically before renderer.render). Optional dt is ignored internally today (reserved for future easing/tween helpers).


DomObjectOptions

| Option | Type | Default | Description | |--------|------|---------|-------------| | anchor | 'center' | 'top-left' | [number, number] | 'center' | Anchor point inside DOM element (0..1 for custom). | | applyDPR | boolean | false | Multiply DOM pixel size by devicePixelRatio before world conversion. | | target | 'objectZ' | 'raycast' | 'objectZ' | How to resolve world position along Z. | | raycastObjects | THREE.Object3D[] | undefined | Required when target='raycast' — intersection candidates. | | preserveZ | boolean | false | Keep object's existing Z (only X/Y updated). |

Non-uniform scaling is applied; Z scale is derived from Y to preserve proportions.


📏 How Mapping Works (Conceptual)

  1. DOM rect → normalized device coordinates (NDC)
  2. NDC unprojected through perspective to a 3D ray
  3. Intersect ray with: plane at objectZ, or meshes (raycast)
  4. Compute pixel→world scale using camera frustum height at object depth
  5. Apply scale & positional anchor offset
  6. Optionally apply depth & rotation attributes

This keeps layout fidelity even when resizing or moving elements.


🧠 Performance Notes

  • O(N) per tracked element, lightweight math only
  • Raycast mode adds intersection cost — keep raycastObjects small or pre-grouped
  • Only reads attributes if present (data-* checks are conditional)
  • Avoid excessive DOM reflow triggers; using transforms for movement is ideal

🗺 Roadmap / Ideas

  • Optional texture auto-binding from <img> / CSS background
  • Built-in tween helpers using dt
  • Support for perspective-correct billboarding mode
  • Utility to batch-manage multiple element/object pairs declaratively

Have a feature request? Open an issue: https://bit.ly/scene-bridge-3d-issues


🤝 Contributing

  1. Fork & clone
  2. Install deps
  3. Build in watch mode:
npm install
npm run build:watch
  1. Open a PR with a clear description & before/after visuals if UI-related.

Repo: https://bit.ly/scene-bridge-3d-git


📜 License

Released under the ISC License. See LICENSE.


🙌 Acknowledgements

  • Three.js
  • Inspiration from various DOM <-> 3D projection techniques in the community

👤 Author

Aayush Chouhan

Aayush Chouhan
GitHub: @aayushchouhan24


🧪 Minimal TypeScript Types Glimpse

import Scene, { DomObjectOptions } from 'scene-bridge-3d';

🆘 FAQ

Do I have to use absolutely positioned elements?
No, any element with a stable layout box works; predictable coordinates help.

Why is my mesh distorted?
Check geometry bounds; zero-sized geometry axes cause large scale factors.

Can I animate with GSAP?
Yes — animate CSS left/top/width/height or data attributes; call scene.update() each frame.

Does it support OrthographicCamera?
Currently tuned for PerspectiveCamera. An orthographic variant could be added (PR welcome).


Enjoy building hybrid interfaces! If this helps you, a star ⭐ on GitHub means a lot.