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

expo-lux-sensor

v0.3.1

Published

A native Expo module for ambient light (lux) measurements. Uses hardware light sensor on Android (with camera fallback) and camera-based estimation on iOS.

Readme

expo-lux-sensor

A native Expo module for ambient light (lux) measurements. Uses hardware light sensor on Android (with camera fallback) and camera-based estimation on iOS.

Features

  • 📱 Cross-platform: Works on both iOS and Android
  • 🔄 Real-time updates: Get continuous light level measurements
  • ⚙️ Configurable: Adjustable update interval and calibration constant
  • 🔐 Permission handling: Built-in permission request and status checking
  • 📊 Event-based: Listen to light level changes via event listeners
  • 💡 Smart sensor selection: Prefers hardware light sensor on Android, falls back to camera when unavailable

Installation

npm install expo-lux-sensor

iOS Setup

After installing the package, run:

npx pod-install

Android Setup

No additional setup required. The module will automatically be linked.

Usage

Basic Example

import {
  addLuxListener,
  startLuxUpdatesAsync,
  stopLuxUpdatesAsync,
  requestPermissionsAsync,
} from 'expo-lux-sensor';
import { useEffect, useState } from 'react';

export default function App() {
  const [lux, setLux] = useState<number | null>(null);

  useEffect(() => {
    // Request permissions
    requestPermissionsAsync();

    // Listen to lux changes
    const subscription = addLuxListener((sample) => {
      setLux(sample.lux);
    });

    // Start sensor updates
    startLuxUpdatesAsync();

    // Cleanup
    return () => {
      subscription.remove();
      stopLuxUpdatesAsync();
    };
  }, []);

  return (
    <View>
      <Text>Light Level: {lux !== null ? `${lux.toFixed(0)} lux` : '--'}</Text>
    </View>
  );
}

Advanced Example with Options

import {
  addLuxListener,
  startLuxUpdatesAsync,
  stopLuxUpdatesAsync,
  requestPermissionsAsync,
  getPermissionsAsync,
} from 'expo-lux-sensor';

async function startLightSensor() {
  // Check permissions first
  const permission = await getPermissionsAsync();
  
  if (!permission.granted) {
    const result = await requestPermissionsAsync();
    if (!result.granted) {
      console.warn('Camera permission is required');
      return;
    }
  }

  // Start with custom options
  await startLuxUpdatesAsync({
    updateInterval: 0.5, // Update every 500ms
    calibrationConstant: 50, // Default calibration (adjust 30-80 based on your needs)
  });

  // Listen to updates
  const subscription = addLuxListener((sample) => {
    console.log(`Lux: ${sample.lux}, Timestamp: ${sample.timestamp}`);
  });

  // Remember to clean up
  // subscription.remove();
  // stopLuxUpdatesAsync();
}

API Reference

Functions

startLuxUpdatesAsync(options?: LuxSensorOptions): Promise<void>

Starts the light sensor updates. Requires camera permission.

Parameters:

  • options (optional): Configuration options
    • updateInterval?: number - Update interval in seconds (default: 0.4)
    • calibrationConstant?: number - Calibration constant for lux calculation (default: 50)

Throws:

  • MissingPermissions - If camera permission is not granted

stopLuxUpdatesAsync(): Promise<void>

Stops the light sensor updates and releases camera resources.

isLuxUpdatesRunningAsync(): Promise<boolean>

Returns whether the sensor is currently running.

getPermissionsAsync(): Promise<PermissionResponse>

Gets the current permission status without requesting.

Returns:

{
  granted: boolean;
  status: 'undetermined' | 'granted' | 'denied';
}

requestPermissionsAsync(): Promise<PermissionResponse>

Requests camera permission from the user.

Returns:

{
  granted: boolean;
  status: 'undetermined' | 'granted' | 'denied';
}

Event Listeners

addLuxListener(listener: (sample: LuxMeasurement) => void): EventSubscription

Adds a listener for lux measurement updates.

Parameters:

  • listener - Callback function that receives LuxMeasurement objects

Returns:

  • EventSubscription - Subscription object with a remove() method

Example:

const subscription = addLuxListener((sample) => {
  console.log(`Lux: ${sample.lux}`);
  console.log(`Timestamp: ${sample.timestamp}`);
});

// Later, remove the listener
subscription.remove();

removeAllListeners(): void

Removes all lux event listeners.

Types

LuxMeasurement

{
  lux: number;        // Light level in lux
  timestamp: number;  // Timestamp in milliseconds
}

LuxSensorOptions

{
  updateInterval?: number;        // Update interval in seconds
  calibrationConstant?: number;   // Calibration constant
}

PermissionResponse

{
  granted: boolean;
  status: 'undetermined' | 'granted' | 'denied';
}

Platform-Specific Notes

iOS

  • Uses AVCaptureDevice to access camera metadata
  • Uses the back camera for light measurements (falls back to default camera if back camera is unavailable)
  • Calculates lux from EXIF data (aperture, exposure time, ISO) using the formula: lux = C × N² / (t × S)
  • Default calibration constant: 50 (optimized for camera-based lux estimation)
  • Requires NSCameraUsageDescription in Info.plist (handled automatically by Expo)

Android

  • Prefers the device light sensor when available (no camera usage, returns hardware lux directly)
  • Falls back to Camera2 API with exposure metadata when no light sensor is available
  • Uses the back camera for light measurements (falls back to first available camera if back camera is unavailable)
  • Calculates lux from exposure metadata (aperture, exposure time, ISO) using the formula: lux = C × N² / (t × S)
  • Default calibration constant: 50 (optimized for camera-based lux estimation)
  • Requires CAMERA permission only when falling back to the camera (handled automatically by Expo)

Calibration Constant

The default calibration constant is 50, optimized for camera-based lux estimation using the ISO 2720 formula:

lux = (C × N²) / (t × S)

Where:

  • C = Calibration constant (default: 50)
  • N = Aperture (f-number)
  • t = Exposure time (seconds)
  • S = ISO sensitivity

Adjustment recommendations:

  • 40-60: Indoor / typical scenes (default: 50)
  • 60-80: Bright outdoor scenes
  • 30-40: Darker environments or if readings seem too high

Note: The minimum measurable lux is not 0. Due to camera physics (minimum exposure time, minimum ISO), even in complete darkness you may see values around 1-10 lux. This is expected behavior.

Lux Reference Values

Use this table to understand what different lux readings mean in real-world conditions:

| Condition | Lux | |-----------|-----| | Full moon | ~0.1 | | Twilight | 1 - 10 | | Corridors, storage | 100 | | Very dark overcast day | 100 - 500 | | Sunrise or sunset (clear) | 300 - 500 | | Office / classroom | 300 - 500 | | Overcast day | 1,000 - 2,000 | | Shade (sunny day) | 5,000 - 10,000 | | Full daylight (indirect) | 10,000 - 25,000 | | Direct sunlight | 30,000 - 100,000 |

Plant growing reference: | Light Level | Lux | Suitable For | |-------------|-----|--------------| | Low light | 500 - 2,500 | ZZ plant, pothos, snake plant, ferns | | Medium light | 2,500 - 10,000 | Philodendron, peace lily | | Bright indirect | 10,000 - 20,000 | Fiddle leaf fig, monstera, rubber plant | | Direct sunlight | 20,000+ | Succulents, cacti, herbs |

Permissions

This module requires camera permission because it uses the device's camera to measure ambient light. The permission is requested automatically when you call requestPermissionsAsync().

Note: The module prioritizes the back camera (rear-facing camera) for measurements. If the back camera is unavailable, it will fall back to the default camera. Make sure the camera is not obstructed and is facing the light source you want to measure.

iOS: Add to app.json:

{
  "ios": {
    "infoPlist": {
      "NSCameraUsageDescription": "This app needs access to the camera to measure ambient light levels."
    }
  }
}

Android: The CAMERA permission is automatically added to your app's AndroidManifest.xml when you install this module. The module's AndroidManifest.xml is automatically merged with your app's manifest during the build process. You don't need to add it manually in app.json.

Troubleshooting

Permission Denied

If you get a permission error:

  1. Make sure you've requested permissions using requestPermissionsAsync()
  2. Check that the permission is granted before calling startLuxUpdatesAsync()
  3. On iOS, verify NSCameraUsageDescription is set in app.json

No Lux Values

If you're not receiving lux values:

  1. Ensure the sensor is started with startLuxUpdatesAsync()
  2. Check that you've added a listener with addLuxListener()
  3. Verify camera permission is granted
  4. Make sure the app is running on a physical device (camera access may not work in simulators)

High Battery Usage

The sensor uses the camera continuously, which can drain battery. Consider:

  • Increasing the updateInterval to reduce update frequency
  • Stopping the sensor when not needed with stopLuxUpdatesAsync()
  • Only starting the sensor when the screen is visible

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT

Author

15bedirhan

Links