New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

r3f-navigation-controls

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

r3f-navigation-controls

Easy-to-use React component for joystick and pointer controls in 3D scenes (Three.js/React Three Fiber). Setup movement, camera rotation, and customizable UI.

latest
Source
npmnpm
Version
1.0.3
Version published
Maintainers
1
Created
Source

r3f-navigation-controls

A production-ready React component library for joystick, keyboard, and pointer controls in 3D scenes with React Three Fiber. Perfect for FPS games, exploration, and mobile 3D apps.

Desktop: WASD + Pointer Lock (FPS style) | Mobile: Left joystick + right-side drag to rotate

✨ Features

  • 🎮 Dual Input Systems

    • Desktop: WASD keyboard + pointer lock (FPS-style mouse look)
    • Mobile: Touch joystick + drag-to-rotate camera
  • 📱 Smart Device Detection

    • Automatically detects mobile vs desktop
    • Responsive to resize/orientation changes
    • No joystick shown on PC, even with touch support
  • 🎨 Fully Customizable UI

    • Joystick size, colors, opacity, position
    • Appearance configuration for branding
  • Camera-Relative Movement

    • Forward/backward/left/right always follow where camera is looking
    • Smooth quaternion-based rotation
  • 🔧 Easy Integration

    • Single <NavigationController /> component
    • One useNavigationSetup() hook call
    • Drop into any React Three Fiber project
  • 📦 No Dependencies

    • Only requires React, Three.js, React Three Fiber
    • Lightweight and tree-shakeable

📦 Installation

npm install r3f-navigation-controls

🚀 Quick Start

The Navigator component + JoystickUI handle everything automatically — mobile detection, joystick UI, pointer lock, all camera movement and rotation:

import { useRef } from 'react';
import { Canvas } from '@react-three/fiber';
import { Navigator, JoystickUI } from 'r3f-navigation-controls';

function Scene() {
  return (
    <>
      <ambientLight intensity={0.5} />
      <pointLight position={[10, 10, 10]} />
      {/* Your 3D scene */}
    </>
  );
}

export default function App() {
  const joystickDataRef = useRef({ x: 0, y: 0 });

  return (
    <>
      <Canvas camera={{ position: [0, 5, 10] }}>
        <Navigator speed={0.18} joystickDataRef={joystickDataRef} />
        <Scene />
      </Canvas>

      <JoystickUI
        joystickDataRef={joystickDataRef}
        appearance={{ joystickSize: 120, primaryColor: '#00ff88' }}
      />
    </>
  );
}

That's it! The package automatically:

  • ✅ Detects if device is mobile or desktop
  • ✅ Shows joystick on mobile only (rendered outside Canvas)
  • ✅ Adds PointerLockControls on desktop
  • ✅ Adds "Enter Pointer Lock" button on desktop
  • ✅ Handles all camera movement and rotation

Advanced Setup (If You Need Custom Control)

If you need more flexibility, use the individual components:

📖 API Reference

<Navigator /> (Inside Canvas)

Handles all movement logic and rendering.

<Canvas>
  <Navigator 
    speed={0.18}
    joystickDataRef={joystickDataRef}
    isMobileOverride={false}  // Optional: force mobile/desktop mode
  />
  <Scene />
</Canvas>

What it does:

  • ✅ Detects mobile vs desktop automatically
  • ✅ Renders PointerLockControls on desktop (inside Canvas)
  • ✅ Handles all camera movement (keyboard + joystick)
  • ✅ Handles all camera rotation (pointer lock + touch drag)
  • ✅ Creates "Enter Pointer Lock" button on desktop
  • ✅ Returns null (headless, no DOM inside Canvas)

Props:

  • speed - Movement speed (default: 0.18)
  • joystickDataRef - Shared ref with JoystickUI (updates when joystick is dragged)
  • isMobileOverride - Force mobile/desktop mode for testing

<JoystickUI /> (Outside Canvas)

Renders the joystick UI outside of Canvas.

<JoystickUI
  joystickDataRef={joystickDataRef}
  appearance={{
    joystickSize: 120,
    primaryColor: '#00ff88',
    secondaryColor: '#00d4ff',
    opacity: 0.6,
    borderRadius: 50,
    borderWidth: 3,
    showLabels: true,
  }}
/>

What it does:

  • ✅ Auto-detects mobile vs desktop
  • ✅ Renders joystick only on mobile devices
  • ✅ Completely outside Canvas (no R3F conflicts)
  • ✅ Updates joystickDataRef in real-time when dragged

Props:

  • joystickDataRef - Shared ref with AppSetup (required)
  • appearance - Customize colors, size, opacity, etc.
    • joystickSize (default: 100)
    • primaryColor (default: '#00ff88')
    • secondaryColor (default: '#00d4ff')
    • opacity (default: 0.6)
    • borderRadius (default: 50)
    • borderWidth (default: 3)
    • showLabels (default: true)

Advanced: Individual Components

If you need more control, you can use the lower-level components:

useNavigationSetup()

const {
  isMobile,           // boolean - device type
  usePointerLock,     // boolean - pointer lock active
  joystickDataRef,    // ref - joystick input data
  requestPointerLock, // function - request pointer lock on desktop
} = useNavigationSetup();

Features:

  • ✅ Mobile detection (UA + touch + coarse + viewport)
  • ✅ Pointer lock state tracking
  • ✅ Resize/orientation listeners
  • ✅ Returns ready-to-use refs and callbacks

Headless controller component (goes inside Canvas).

<Navigator 
  joystickDataRef={joystickDataRef}
  speed={0.18}
  isMobileOverride={false}  // Optional: force mobile/desktop mode
/>

What it does:

  • Handles all camera movement (joystick on mobile, WASD on desktop)
  • Manages touch rotation on mobile
  • Returns null (no visible DOM)

<JoystickController />

Renders the joystick UI (goes outside Canvas).

<JoystickController
  isMobile={true}
  joystickDataRef={joystickDataRef}
  appearance={{
    joystickSize: 120,
    primaryColor: '#00ff88',
    secondaryColor: '#00d4ff',
    opacity: 0.6,
    borderRadius: 50,
    borderWidth: 3,
    showLabels: true,
  }}
/>

Props:

  • isMobile - Show joystick when true
  • joystickDataRef - Shared ref with NavigationController
  • appearance - Customize colors, size, position

useKeyboard()

Get keyboard state (used internally, but available).

const keyboard = useKeyboard();
// keyboard.forward, keyboard.backward, keyboard.left, keyboard.right
// keyboard.shift, keyboard.space

usePointerControls(config?)

Get pointer/touch rotation data (used internally, but available).

const pointerRotation = usePointerControls({
  sensitivity: 0.002,
  minAngle: -Math.PI / 3,
  maxAngle: Math.PI / 3,
});

🎮 Control Scheme

Desktop

ActionKey(s)
Move ForwardW /
Move BackwardS /
Strafe LeftA /
Strafe RightD /
Look Up/DownMouse (after pointer lock)
Enable Pointer LockClick "Enter Pointer Lock" button
Exit Pointer LockESC

Mobile

ActionInput
MoveDrag left joystick
Rotate CameraDrag right side of screen
Look aroundMulti-direction drag

🎨 Customization Examples

Fast Movement Speed

<NavigationController speed={0.3} joystickDataRef={joystickDataRef} />

Vibrant Neon Joystick

<JoystickController
  isMobile={true}
  joystickDataRef={joystickDataRef}
  appearance={{
    joystickSize: 140,
    primaryColor: '#ff00ff',
    secondaryColor: '#00ffff',
    opacity: 0.8,
    borderRadius: 100,
    borderWidth: 4,
  }}
/>

Force Desktop Mode (for testing)

<NavigationController 
  joystickDataRef={joystickDataRef} 
  isMobileOverride={false}
/>

📱 Device Detection Logic

The package automatically detects mobile devices by checking:

  • User-Agent: Mobile OS strings (Android, iOS, Windows Phone, etc.)
  • Touch Support + Pointer Type: Has touch AND coarse pointer
  • Viewport Size: ≤ 900px width (small screen)

Result:

  • ✅ Joystick shown on: Real phones, tablets, mobile browsers
  • ❌ Joystick hidden on: Desktop, touch-enabled laptops (large screens)

🔌 Full Example (Manual Control)

For advanced use cases where you want to use individual components:

import { useRef } from 'react';
import { Canvas } from '@react-three/fiber';
import { PointerLockControls } from '@react-three/drei';
import { 
  NavigationController, 
  JoystickController, 
  useNavigationSetup 
} from 'r3f-navigation-controls';

function Scene() {
  return (
    <>
      <ambientLight intensity={0.5} />
      <pointLight position={[10, 10, 10]} />
      {/* Add your 3D models and scene here */}
    </>
  );
}

export default function App() {
  const { isMobile, usePointerLock, joystickDataRef, requestPointerLock } = useNavigationSetup();

  return (
    <>
      <Canvas camera={{ position: [0, 5, 10] }}>
        <Scene />
        <Navigator joystickDataRef={joystickDataRef} />
        {!isMobile && <PointerLockControls />}
      </Canvas>

      {!isMobile && !usePointerLock && (
        <button 
          onClick={requestPointerLock}
          style={{
            position: 'absolute',
            top: '20px',
            right: '20px',
            padding: '10px 20px',
            cursor: 'pointer',
          }}
        >
          Enable Pointer Lock
        </button>
      )}

      {isMobile && (
        <JoystickController
          isMobile={true}
          joystickDataRef={joystickDataRef}
          appearance={{
            joystickSize: 120,
            primaryColor: '#00ff88',
            secondaryColor: '#00d4ff',
            opacity: 0.6,
          }}
        />
      )}
    </>
  );
}

🛠️ Advanced: Custom Movement Logic

If you need to override movement behavior, you can access the raw hooks:

import { useKeyboard, usePointerControls } from 'r3f-navigation-controls';
import { useFrame } from '@react-three/fiber';
import { Vector3 } from 'three';

function CustomController() {
  const keyboard = useKeyboard();
  const pointerRotation = usePointerControls();

  useFrame(({ camera }) => {
    const forward = new Vector3(0, 0, -1).applyQuaternion(camera.quaternion);
    if (keyboard.forward) camera.position.add(forward.multiplyScalar(0.1));
  });

  return null;
}

🤝 Contributing

Issues and PRs welcome! This project is maintained on GitHub.

📄 License

MIT

🙏 Credits

Built for React Three Fiber projects using Three.js and modern React hooks.

Keywords

react

FAQs

Package last updated on 17 Nov 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts