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

react-native-nitro-ar

v2026.2.2

Published

Nitro module for ARKit (iOS)

Readme

react-native-nitro-ar

A high-performance Nitro module that provides ARKit functionality for React Native on iOS.

Features

  • AR Session Management - Start, pause, and reset AR sessions with full configuration control
  • Plane Detection - Detect horizontal and vertical planes with classification (floor, wall, ceiling, table, seat, window, door)
  • Raycasting - Hit-test against detected planes and estimated surfaces
  • Anchors - Create and track anchors in 3D space
  • Measurements - Measure distances between anchor points
  • Bounding Boxes - Calculate oriented bounding boxes (OBB) using PCA
  • Light Estimation - Access ambient and directional light data with spherical harmonics
  • LiDAR Depth - Access scene depth and smoothed depth data (iOS 14+, LiDAR devices)
  • World Maps - Save and restore AR world maps for persistent AR experiences
  • Camera Data - Access camera pose, intrinsics, projection/view matrices

Requirements

  • React Native 0.78.0+
  • Node 18.0.0+
  • iOS 13.0+ (iOS 14.0+ for LiDAR depth features)

Installation

npm install react-native-nitro-ar react-native-nitro-modules

Expo

If you're using Expo, add the plugin to your app.json:

{
  "expo": {
    "plugins": ["react-native-nitro-ar"]
  }
}

Then run prebuild:

npx expo prebuild

Bare React Native

Add camera usage description to your Info.plist:

<key>NSCameraUsageDescription</key>
<string>This app uses the camera for AR experiences</string>

Then install pods:

cd ios && pod install

Usage

Basic Session

import { createARSession } from "react-native-nitro-ar";

const session = createARSession();

// Start with default configuration
session.start();

// Or with custom configuration
session.start({
  planeDetection: ["horizontal", "vertical"],
  lightEstimation: true,
  environmentTexturing: "automatic",
  worldAlignment: "gravity",
});

// Pause/resume
session.pause();
session.start();

// Reset tracking
session.reset();

Plane Detection

// Get all detected planes
const planes = session.planeAnchors;

for (const plane of planes) {
  console.log("Plane:", plane.identifier);
  console.log("Classification:", plane.classification); // floor, wall, table, etc.
  console.log("Alignment:", plane.alignment); // horizontal or vertical
  console.log("Extent:", plane.extent); // [width, height]
  console.log("Center:", plane.center); // [x, y, z]

  // Access plane geometry for rendering
  const geo = plane.geometry;
  console.log("Vertices:", geo.vertices);
  console.log("Indices:", geo.triangleIndices);
}

// Subscribe to plane updates
const unsubscribe = session.onPlanesUpdated((added, updated, removedIds) => {
  console.log("Added planes:", added.length);
  console.log("Updated planes:", updated.length);
  console.log("Removed plane IDs:", removedIds);
});

// Later: unsubscribe()

Raycasting

// Simple raycast (normalized screen coordinates 0-1)
const hit = session.raycast(0.5, 0.5);

if (hit) {
  console.log("Hit position:", hit.position);
  console.log("Hit rotation:", hit.rotation);
  console.log("Distance:", hit.distance);

  // Create an anchor at the hit location
  const anchor = session.createAnchor(hit);
}

// Advanced raycast with options
const results = session.raycastWithQuery({
  x: 0.5,
  y: 0.5,
  target: "existingPlaneGeometry", // or 'existingPlaneInfinite', 'estimatedPlane', 'any'
  alignment: "horizontal", // or 'vertical', 'any'
});

Anchors

// Create anchor from raycast
const anchor = session.createAnchor(raycastResult);

// Create anchor at specific position
const anchor = session.createAnchorAtPosition(
  [0, 0, -1], // position [x, y, z]
  [0, 0, 0, 1], // rotation quaternion [x, y, z, w]
);

// Access anchor properties
console.log("ID:", anchor.identifier);
console.log("Position:", anchor.position);
console.log("Rotation:", anchor.rotation);
console.log("Is tracked:", anchor.isTracked);

// Remove anchor
session.removeAnchor(anchor);

// Subscribe to anchor updates
const unsubscribe = session.onAnchorsUpdated((added, updated, removedIds) => {
  // Handle anchor changes
});

Measurements

// Create measurement between two anchors
const measurement = session.createMeasurement(startAnchor, endAnchor);

console.log("Length (meters):", measurement.length);
console.log("Is valid:", measurement.isValid);

Frame Updates

// Subscribe to frame updates
const unsubscribe = session.onFrameUpdate((frame) => {
  console.log("Timestamp:", frame.timestamp);
  console.log("Camera position:", frame.cameraPosition);
  console.log("Camera rotation:", frame.cameraRotation);

  // Light estimation
  if (frame.lightEstimate) {
    console.log("Ambient intensity:", frame.lightEstimate.ambientIntensity);
    console.log("Color temperature:", frame.lightEstimate.ambientColorTemperature);
  }

  // Directional light (for realistic lighting)
  if (frame.directionalLightEstimate) {
    console.log("Light direction:", frame.directionalLightEstimate.primaryLightDirection);
    console.log(
      "Spherical harmonics:",
      frame.directionalLightEstimate.sphericalHarmonicsCoefficients,
    );
  }

  // LiDAR depth (iOS 14+, devices with LiDAR)
  if (frame.sceneDepth) {
    const depth = frame.sceneDepth;
    console.log("Depth map size:", depth.width, "x", depth.height);

    // Get depth at specific point
    const depthValue = depth.getDepthAt(0.5, 0.5);
    const confidence = depth.getConfidenceAt(0.5, 0.5);
  }

  // Capture camera image
  const base64Image = frame.getCapturedImage(0.8); // quality 0-1
});

Tracking State

// Check current state
console.log("Is running:", session.isRunning);
console.log("Tracking state:", session.trackingState); // 'normal', 'limited', 'notAvailable'
console.log("Tracking reason:", session.trackingStateReason); // 'none', 'initializing', 'excessiveMotion', etc.
console.log("World mapping:", session.worldMappingStatus); // 'notAvailable', 'limited', 'extending', 'mapped'

// Subscribe to tracking changes
const unsubscribe = session.onTrackingStateChanged((state, reason) => {
  if (state === "limited") {
    console.log("Tracking limited:", reason);
  }
});

World Map Persistence

// Save world map (for relocalization later)
const worldMap = await session.getCurrentWorldMap();
const mapData = worldMap.getData(); // base64 encoded

// Store mapData somewhere (file, cloud, etc.)
await saveToStorage(mapData);

// Later: restore session with saved map
const savedMapData = await loadFromStorage();
session.start({
  initialWorldMap: savedMapData,
});

Bounding Box Builder

import { createARBoundingBoxBuilder } from "react-native-nitro-ar";

const builder = createARBoundingBoxBuilder();

// Add points (from raycasts, anchors, etc.)
builder.addPoint([x, y, z]);
builder.addPoints([
  [x1, y1, z1],
  [x2, y2, z2],
  [x3, y3, z3],
]);

// Get oriented bounding box (uses PCA for optimal orientation)
const obb = builder.getOrientedBoundingBox();
console.log("Center:", obb.center);
console.log("Half extents:", obb.halfExtents);
console.log("Axes:", obb.axes); // 3 orthogonal direction vectors

// Reset for new calculation
builder.clear();

API Reference

ARSession

| Property/Method | Type | Description | | ----------------------------------- | --------------------- | --------------------------------- | | start(config?) | void | Start AR session | | pause() | void | Pause AR session | | reset() | void | Reset tracking and remove anchors | | isRunning | boolean | Session running state | | trackingState | TrackingState | Current tracking quality | | trackingStateReason | TrackingStateReason | Reason for limited tracking | | worldMappingStatus | WorldMappingStatus | World map quality | | getCameraPose() | CameraPose | Current camera position/rotation | | currentFrame | ARFrame? | Latest frame data | | raycast(x, y) | ARRaycastResult? | Hit-test at screen point | | raycastWithQuery(query) | ARRaycastResult[] | Advanced hit-test | | createAnchor(hit) | ARAnchor | Create anchor from raycast | | createAnchorAtPosition(pos, rot?) | ARAnchor | Create anchor at position | | removeAnchor(anchor) | void | Remove anchor | | anchors | ARAnchor[] | All anchors | | planeAnchors | ARPlaneAnchor[] | Detected planes | | createMeasurement(start, end) | ARMeasurement | Measure between anchors | | getCurrentWorldMap() | Promise<ARWorldMap> | Get world map for persistence | | onFrameUpdate(callback) | () => void | Subscribe to frame updates | | onTrackingStateChanged(callback) | () => void | Subscribe to tracking changes | | onAnchorsUpdated(callback) | () => void | Subscribe to anchor changes | | onPlanesUpdated(callback) | () => void | Subscribe to plane changes |

ARSessionConfiguration

| Property | Type | Default | Description | | ---------------------- | ----------------------- | ---------------------------- | --------------------------- | | planeDetection | PlaneDetectionMode[]? | ['horizontal', 'vertical'] | Planes to detect | | lightEstimation | boolean? | true | Enable light estimation | | environmentTexturing | EnvironmentTexturing? | - | Environment texturing mode | | worldAlignment | WorldAlignment? | 'gravity' | World coordinate alignment | | sceneDepth | boolean? | false | Enable LiDAR depth | | smoothedSceneDepth | boolean? | false | Enable smoothed depth | | initialWorldMap | string? | - | Base64 world map to restore |

Development

# Install dependencies
bun install

# Run codegen (generates Nitro bridge code)
bun run codegen

# Type check and build
bun run build

To run the example app:

# from root
bun link
cd example
bun install
bun run ios --device

Physical devices are required.

License

MIT