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

@passeriform/three-tour-controls

v0.1.14

Published

Camera controller for navigating through a sequence of framed views with smooth scroll-based transitions.

Readme

TourControls

TourControls is a lightweight, customizable control for Three.js that allows you to navigate through a series of predefined "poses" using scroll events. This control provides an intuitive way to cycle through different camera positions or orientations in your 3D scene like a tour.

Installation

You can install TourControls via npm:

npm install @passeriform/three-tour-controls

Or, if you use Yarn:

yarn add @passeriform/three-tour-controls

Usage

To use TourControls in your Three.js project, follow these steps:

1. Import TourControls

First, make sure you import both Three.js and TourControls.

import * as THREE from "three"
import TourControls from "@passeriform/three-tour-controls"

2. Set Up the Camera and Scene

Create a basic Three.js scene and camera.

const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
const renderer = new THREE.WebGLRenderer()
document.body.appendChild(renderer.domElement)
renderer.setSize(window.innerWidth, window.innerHeight)

// Add some objects to your scene (optional)
const geometry = new THREE.BoxGeometry()
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
const cube = new THREE.Mesh(geometry, material)
scene.add(cube)

3. Define an Itinerary

Each location in the itinerary is a set of meshes and a target quaternion. The camera will move to frame the meshes from the given orientation.

const itinerary = [
    {
        meshes: [mesh1],
        quaternion: new THREE.Quaternion().setFromEuler(new THREE.Euler(0, Math.PI / 4, 0)),
    },
    {
        meshes: [mesh2a, mesh2b],
        quaternion: new THREE.Quaternion().setFromEuler(new THREE.Euler(0, Math.PI / 2, 0)),
    },
    {
        meshes: [mesh3],
        quaternion: new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI / 4, 0, 0)),
    },
]

4. Initialize TourControls

Attach the TourControls instance to the camera.

const controls = new TourControls(camera, renderer.domElement)

controls.setItinerary(itinerary)

controls.timing = 1000 // default: 400

5. Navigation

Use the mouse wheel to navigate through the itinerary. The camera will animate to each location.

function animate() {
    requestAnimationFrame(animate)

    // Update the controls
    controls.update()

    // Render the scene from the camera's perspective
    renderer.render(scene, camera)
}

animate()

6. Handling Window Resize

Camera aspect ratio is automatically updated when window is resized.

7. Updating the Itinerary

You can update the itinerary at any time:

controls.setItinerary(newItinerary)

8. Detour Navigation (Advanced)

You can temporarily navigate to a "detour" location without changing the main itinerary. This is useful for highlighting or focusing on a mesh outside the main tour.

controls.detour({
    meshes: [specialMesh],
    quaternion: new THREE.Quaternion().setFromEuler(new THREE.Euler(0, 0, 0)),
})

// To end the detour and return to the main tour:
controls.endDetour()

API

TourControls(camera: THREE.PerspectiveCamera, domElement?: HTMLElement)

  • camera: The Three.js perspective camera you want to control.
  • domElement: The HTML element to attach event listeners to (optional).

controls.setItinerary(locations: Array<{ meshes: THREE.Mesh[], quaternion: THREE.Quaternion }>)

Set the base locations the control can move the camera to along with their target orientation.

  • locations: Array of objects representing locations. Each object should have:
    • meshes: Array of meshes to frame.
    • quaternion: Target orientation for the camera.

controls.detour(location: { meshes: THREE.Mesh[], quaternion: THREE.Quaternion })

Temporarily navigate to a detour location. The detour can be ended with controls.endDetour().

controls.endDetour()

End the current detour and return to the main itinerary.

controls.detourExitCondition: "first" | "last"

Controls when detour navigation ends: at the first or last detour location. Default: "last".

controls.detourExitStrategy: "same" | "next" | "first" | "last"

Controls where to return in the main itinerary after ending a detour. Default: "same".

controls.timing: number

The speed of animating the camera across locations in milliseconds. Default: 400.

controls.connect(element: HTMLElement)

Manually attach event listeners to a specific DOM element. This is called automatically in the constructor if a domElement is provided, but can be used to re-attach listeners if needed.

controls.update(time?: number)

Updates the internal tween animations. Call this in your render loop.

controls.clear()

Clears all animations and disposes of the controls.

Example

import * as THREE from "three"
import TourControls from "@passeriform/three-tour-controls"

const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
const renderer = new THREE.WebGLRenderer()
document.body.appendChild(renderer.domElement)

const cube = new THREE.Mesh(new THREE.BoxGeometry(), new THREE.MeshBasicMaterial({ color: 0x00ff00 }))
const sphere = new THREE.Mesh(new THREE.SphereGeometry(), new THREE.MeshBasicMaterial({ color: 0xff0000 }))
const torus = new THREE.Mesh(new THREE.TorusGeometry(), new THREE.MeshBasicMaterial({ color: 0x0000ff }))
scene.add(cube, sphere, torus)

const itinerary = [
    {
        meshes: [cube, sphere],
        quaternion: new THREE.Quaternion().setFromEuler(new THREE.Euler(0, Math.PI / 4, 0)),
    },
    {
        meshes: [torus],
        quaternion: new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.PI / 6, Math.PI / 2, 0)),
    },
    {
        meshes: [cube, torus],
        quaternion: new THREE.Quaternion().setFromEuler(new THREE.Euler(0, 0, Math.PI / 3)),
    },
]

const controls = new TourControls(camera, renderer.domElement)
controls.setItinerary(itinerary)
controls.timing = 1000

// Example: Detour navigation
controls.detour({
    meshes: [cube],
    quaternion: new THREE.Quaternion().setFromEuler(new THREE.Euler(0, 0, 0)),
})

// ... later
controls.endDetour()

function animate() {
    requestAnimationFrame(animate)
    controls.update()
    renderer.render(scene, camera)
}

animate()

window.addEventListener("resize", () => {
    renderer.setSize(window.innerWidth, window.innerHeight)
})

License

MIT License. See LICENSE for details.