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

shader-mouse

v1.0.0

Published

Mouse UV tracking for Three.js and React Three Fiber shaders

Readme

shader-mouse


shader-mouse tracks pointer position as UV coordinates on 3D meshes and updates shader uniforms automatically. Create interactive GLSL effects, hover states, and click-driven visuals with accurate UV coordinates that work seamlessly across any geometry.

✨ Features

| Feature | Description | |---------|-------------| | 🎯 Universal geometry support | Works with PlaneGeometry, SphereGeometry, BoxGeometry, TorusGeometry, and custom shapes | | 🎨 Accurate UV mapping | Uses raycast intersection UV for perfect coordinate tracking on any mesh | | 🌊 Seamless plane projection | Fallback system for continuous effects across multiple flat surfaces | | ⚡ Optimized performance | Pointer events, cached DOMRect, pre-bound handlers, minimal recomputation | | 🔧 Flexible uniforms | Auto-detects and updates vec2, vec3, vec4 uniforms | | 📱 Touch & mobile ready | Full touch/pointer event support with passive listeners | | 🎭 Hover & click states | Built-in interaction detection (hover/click only when over mesh) | | 🎨 Framework agnostic | Works with vanilla Three.js and React Three Fiber | | 📦 Multi-format builds | ESM, CommonJS, UMD bundles + TypeScript declarations | | 🌐 CDN ready | Direct browser usage via UNPKG/JSDelivr |

📦 Installation

# npm
npm install shader-mouse three

# yarn
yarn add shader-mouse three

# pnpm
pnpm add shader-mouse three

CDN Usage (UMD)

<script src="https://unpkg.com/[email protected]/build/three.min.js"></script>
<script src="https://unpkg.com/shader-mouse/dist/shader-mouse.umd.js"></script>

<script>
  // ShaderMouse available as global
  const { ShaderMouse } = window.ShaderMouse;
  // ... use as in examples
</script>

🚀 Quick Start (Three.js)

import * as THREE from 'three'
import { ShaderMouse } from 'shader-mouse'

const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, innerWidth / innerHeight, 0.1, 100)
camera.position.z = 3

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

const material = new THREE.ShaderMaterial({
  uniforms: { 
    uMouse: { value: new THREE.Vector4(0, 0, 0, 0) } // xy=uv, z=hover, w=click
  },
  vertexShader: `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    uniform vec4 uMouse;
    varying vec2 vUv;
    void main() {
      float dist = distance(vUv, uMouse.xy);
      float spot = smoothstep(0.3, 0.0, dist);
      vec3 color = vec3(0.2) + vec3(spot) + vec3(0.3) * uMouse.z * spot;
      gl_FragColor = vec4(color, 1.0);
    }
  `
})

const mesh = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), material)
scene.add(mesh)

const shaderMouse = new ShaderMouse({ camera, domElement: renderer.domElement })
shaderMouse.add(mesh)

const animate = () => {
  requestAnimationFrame(animate)
  renderer.render(scene, camera)
}
animate()

⚛️ React Three Fiber

import { useRef, useEffect, useMemo } from 'react'
import { Canvas, useThree } from '@react-three/fiber'
import * as THREE from 'three'
import { ShaderMouse } from 'shader-mouse'

function Plane({ shaderMouse }) {
  const meshRef = useRef()
  
  const uniforms = useMemo(() => ({
    uMouse: { value: new THREE.Vector4(0, 0, 0, 0) }
  }), [])

  useEffect(() => {
    if (meshRef.current && shaderMouse) {
      shaderMouse.add(meshRef.current)
      return () => shaderMouse.remove(meshRef.current)
    }
  }, [shaderMouse])

  return (
    <mesh ref={meshRef}>
      <planeGeometry args={[2, 2]} />
      <shaderMaterial uniforms={uniforms} vertexShader={'...'} fragmentShader={'...'} />
    </mesh>
  )
}

function Scene() {
  const { camera, gl } = useThree()
  const shaderMouseRef = useRef()

  useEffect(() => {
    shaderMouseRef.current = new ShaderMouse({ domElement: gl.domElement, camera })
    return () => shaderMouseRef.current?.dispose()
  }, [camera, gl])

  return <Plane shaderMouse={shaderMouseRef.current} />
}

export default function App() {
  return (
    <Canvas camera={{ position: [0, 0, 3] }}>
      <Scene />
    </Canvas>
  )
}

📖 API Reference

Constructor Options

interface ShaderMouseOptions {
  domElement?: HTMLElement  // Target element (default: document.body)
  camera: Camera           // Three.js camera (required)
  autoUpdate?: boolean     // Auto-update on RAF (default: true)
}

ShaderMouse Methods

add(object: Object3D, uniformName?: string): this

Adds a mesh to mouse tracking.

  • object: Three.js Object3D (mesh)
  • uniformName: Shader uniform name (default: "uMouse")
shaderMouse.add(mesh, 'uMousePosition')

remove(object: Object3D): this

Removes a mesh from mouse tracking.

shaderMouse.remove(mesh)

update(): void

Manually update all tracked objects (only needed if autoUpdate: false).

shaderMouse.update()

getState(object: Object3D): MouseState | null

Get current mouse state for specific object.

const state = shaderMouse.getState(mesh)
// { uv: Vector2, isHover: boolean, isPressed: boolean }

resize(): void

Update cached DOM element bounds (call on window resize).

window.addEventListener('resize', () => shaderMouse.resize())

dispose(): void

Clean up resources and remove event listeners.

shaderMouse.dispose()

Properties

enabled: boolean

Enable/disable mouse tracking.

shaderMouse.enabled = false // Pause tracking

camera: Camera

Get/set active camera.

shaderMouse.camera = newCamera

🎨 Uniform Types

The library automatically detects and updates different uniform types:

vec2 - UV Only

uniform vec2 uMouse; // x=u, y=v

vec3 - UV + State

uniform vec3 uMouse; // x=u, y=v, z=state (0=none, 0.5=hover, 1=click)

vec4 - UV + Hover + Click

uniform vec4 uMouse; // x=u, y=v, z=hover, w=click

🎮 Interactive Examples

Spotlight Effect

uniform vec4 uMouse;
varying vec2 vUv;

void main() {
  float dist = distance(vUv, uMouse.xy);
  float spotlight = smoothstep(0.3, 0.0, dist);
  
  vec3 color = vec3(0.1);
  color += vec3(0.5) * spotlight;
  color += vec3(0.3) * uMouse.z * spotlight; // Hover glow
  color += vec3(1.0) * uMouse.w * spotlight; // Click flash
  
  gl_FragColor = vec4(color, 1.0);
}

Ripple Effect

uniform vec4 uMouse;
uniform float uTime;
varying vec2 vUv;

void main() {
  float dist = distance(vUv, uMouse.xy);
  float ripple = sin(dist * 20.0 - uTime * 5.0) * 0.5 + 0.5;
  
  vec3 color = vec3(ripple * uMouse.z); // Only show on hover
  gl_FragColor = vec4(color, 1.0);
}

Displacement Effect

// Vertex Shader
uniform vec4 uMouse;
varying vec2 vUv;

void main() {
  vUv = uv;
  
  vec3 pos = position;
  float dist = distance(uv, uMouse.xy);
  float displacement = smoothstep(0.3, 0.0, dist) * uMouse.w; // On click
  pos.z += displacement * 0.5;
  
  gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}

🏗️ Advanced Usage

Multiple Meshes

const shaderMouse = new ShaderMouse({ camera, domElement: canvas })

// Different uniform names
shaderMouse.add(mesh1, 'uMouse')
shaderMouse.add(mesh2, 'uPointer')
shaderMouse.add(mesh3, 'uCursor')

Manual Updates

const shaderMouse = new ShaderMouse({ 
  camera, 
  domElement: canvas,
  autoUpdate: false // Disable auto-update
})

// Manual control in render loop
function animate() {
  shaderMouse.update()
  renderer.render(scene, camera)
  requestAnimationFrame(animate)
}

Custom DOM Element

const canvas = document.getElementById('my-canvas')
const shaderMouse = new ShaderMouse({ 
  camera, 
  domElement: canvas // Custom target
})

Geometry Support

Works with any Three.js geometry:

// All supported
new THREE.PlaneGeometry(2, 1)      // ✅ Any size
new THREE.SphereGeometry(1, 32, 16) // ✅ Curved surfaces  
new THREE.BoxGeometry(1, 1, 1)      // ✅ Multiple faces
new THREE.TorusGeometry(1, 0.3)     // ✅ Complex shapes
new THREE.CylinderGeometry(1, 1, 2) // ✅ Custom geo

🔧 TypeScript Support

Full TypeScript support with exported types:

import { ShaderMouse, type ShaderMouseOptions, type MouseState, type TrackedObject } from 'shader-mouse'

const options: ShaderMouseOptions = {
  camera: myCamera,
  domElement: myCanvas,
  autoUpdate: true
}

const shaderMouse = new ShaderMouse(options)
const state: MouseState = shaderMouse.getState(mesh)!

⚡ Performance Tips

  1. Use pointer events: Built-in for better mobile performance
  2. Cached DOM rect: Automatically cached, manually update with resize()
  3. Disable auto-update: Use autoUpdate: false for manual control
  4. Limit tracked objects: Only add meshes that need mouse interaction
  5. Passive listeners: All events use { passive: true }

📁 Example Projects

Check the examples/ folder:

To run examples:

cd examples/vanilla && npm install && npm run dev
# or
cd examples/react && npm install && npm run dev

🤝 Contributing

Contributions are welcome! Feel free to:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📄 License

MIT © Aayush Chouhan

🌟 Author