Skip to content

tristanmanchester/react-native-dotgrid

Repository files navigation

react-native-dotgrid

Wave Animation Snake Animation

Animated dot-matrix display component for React Native, inspired by ElevenLabs UI Matrix. The default renderer is now a native-first Skia path with a memoized background and worklet-safe Picture foreground, plus an explicit lazy SVG fallback when you need parity or regression comparison.

npm version MIT License

Features

  • Skia by Default - Native-first Picture rendering with a static background and UI-thread foreground updates
  • SVG Fallback - Lazy compatibility path available via renderer="svg"
  • UI Thread Animations - Smooth 60fps animations via Reanimated
  • VU Meter Mode - Real-time audio visualization support
  • Customizable - Full control over colors, sizing, and timing
  • Pre-built Presets - Wave, loader, pulse, snake, ripple animations
  • Cross-Platform - iOS, Android, and Web support
  • Accessible - Screen reader support with ARIA labels

Showcase

Showcase Grid

Multiple patterns and animations running simultaneously

Installation

npm install react-native-dotgrid @shopify/react-native-skia react-native-reanimated react-native-worklets

If you want the compatibility renderer as well:

npm install react-native-svg

Configuration

Install Reanimated and Skia with your platform tooling so versions stay compatible with your app. For Expo projects:

npx expo install @shopify/react-native-skia react-native-reanimated react-native-worklets

If your app still requires manual Reanimated Babel setup, keep the plugin last:

// babel.config.js
module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: [
    // ... other plugins
    'react-native-reanimated/plugin' // ← Must be last
  ]
};

For Expo projects, see example/babel.config.js for a working setup.

For web, Skia requires CanvasKit setup. Follow the official React Native Skia web instructions.

Workspace Development

This repo is now an npm workspace. Install and run everything from the workspace root:

npm install
npm run typecheck
npm test
npm run build

Run the Expo example app from the root with:

npm run example:web
npm run example:ios

Before publishing, run the packed-artifact smoke test from the root:

npm run smoke:pack

That flow builds the library, packs it, installs the tarball into a clean temporary Expo app, runs expo-doctor, verifies iOS prebuild, and exports the web bundle. It is the release check that best matches how npm consumers will actually install the package.

Quick Start

import React from 'react';
import { View } from 'react-native';
import { Matrix, waveFrames, digits } from 'react-native-dotgrid';

export default function App() {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      {/* Static digit */}
      <Matrix rows={7} cols={5} pattern={digits[5]} />

      {/* Animated wave */}
      <Matrix rows={7} cols={7} frames={waveFrames} fps={20} loop />

      {/* Force the SVG fallback if needed */}
      <Matrix rows={7} cols={7} frames={waveFrames} fps={20} loop renderer="svg" />
    </View>
  );
}

Usage Examples

Static Patterns

Display a single static pattern:

import { Matrix, digits, chevronLeft } from 'react-native-dotgrid';

// Display number 5
<Matrix rows={7} cols={5} pattern={digits[5]} />

// Direction indicator
<Matrix rows={7} cols={7} pattern={chevronLeft()} />

Animated Presets

Use pre-generated animations for common patterns:

import { Matrix, waveFrames, loaderFrames, snakeFrames } from 'react-native-dotgrid';

// Wave animation
<Matrix rows={7} cols={7} frames={waveFrames} fps={20} loop />

// Loading spinner
<Matrix rows={7} cols={12} frames={loaderFrames} fps={18} loop />

// Snake with fading tail
<Matrix rows={7} cols={12} frames={snakeFrames} fps={20} loop />

Dynamic Generation

Generate patterns with custom dimensions:

import { Matrix, generateWaveFrames, pulse, ripple } from 'react-native-dotgrid';

// Custom wave size
const customWave = generateWaveFrames(10, 10, { length: 30 });
<Matrix rows={10} cols={10} frames={customWave} fps={20} loop />

// Pulse effect
<Matrix rows={8} cols={8} frames={pulse(8, 8, 16)} fps={16} loop />

// Ripple effect with options
const rippleFrames = ripple(9, 9, {
  length: 48,
  wavelength: 3.5,
  damping: 0.06
});
<Matrix rows={9} cols={9} frames={rippleFrames} fps={24} loop />

VU Meter Mode

Create audio visualizations with real-time level updates:

import { Matrix } from 'react-native-dotgrid';
import { useEffect, useState } from 'react';

function AudioVisualizer() {
  const [levels, setLevels] = useState(Array(12).fill(0));

  useEffect(() => {
    // Update levels from audio input
    const interval = setInterval(() => {
      setLevels(Array.from({ length: 12 }, () => Math.random()));
    }, 80);
    return () => clearInterval(interval);
  }, []);

  return (
    <Matrix
      rows={7}
      cols={12}
      mode="vu"
      levels={levels}
      palette={{
        on: '#00ff00',
        off: '#003300',
        background: '#000'
      }}
    />
  );
}

Static VU Pattern

Create static VU meter displays using the vu() helper:

import { Matrix, vu } from 'react-native-dotgrid';

// Display static levels
const levels = [0.2, 0.5, 0.8, 1.0, 0.7, 0.4, 0.1];
<Matrix rows={7} cols={7} pattern={vu(7, levels)} />

Custom Styling

Customize colors, sizing, and spacing:

<Matrix
  rows={7}
  cols={7}
  frames={waveFrames}
  renderer="skia"    // Default renderer
  size={12}          // Dot diameter in pixels
  gap={3}            // Space between dots
  brightness={0.8}   // Global brightness multiplier
  palette={{
    on: '#00ffff',   // Active dot color
    off: '#001122',  // Inactive dot color
    background: 'transparent'
  }}
  fps={30}
  loop
/>

API Reference

Matrix Component Props

Prop Type Default Description
rows number required Number of rows in the grid
cols number required Number of columns in the grid
renderer 'skia' | 'svg' 'skia' Rendering backend
pattern Frame - Single frame to display (static)
frames Frame[] - Array of frames for animation
fps number 12 Animation frames per second
autoplay boolean true Start animation on mount
loop boolean true Loop animation continuously
paused boolean false Pause/resume animation
size number 10 Dot diameter in pixels
gap number 2 Space between dots in pixels
palette Palette See below Color configuration
brightness number 1 Global brightness (0-1)
mode 'default' | 'vu' 'default' Rendering mode
levels number[] - VU meter levels (0-1) per column
onFrame (index: number) => void - Frame change callback
accessibilityLabel string - Screen reader label

Types

type Frame = number[][];  // 2D array, values 0-1 (brightness)

type Palette = {
  on: string;         // Active dot color
  off: string;        // Inactive dot color
  background?: string; // Optional background
};

Pre-generated Constants

All pre-generated at 7×7 dimensions for consistency:

  • digits - Array of 10 digit patterns (0-9), each 7×5
  • waveFrames - 24-frame sine wave animation
  • loaderFrames - Perimeter loading spinner
  • pulseFrames - 16-frame global brightness pulse
  • snakeFrames - 49-frame snake with fading tail
  • rippleFrames - 24-frame concentric ripple effect
  • chevronLeftFrame - Static left arrow
  • chevronRightFrame - Static right arrow

Generator Functions

Create patterns at any dimensions:

// Animations
generateWaveFrames(rows, cols, options?)
generateLoaderFrames(rows, cols)
generatePulseFrames(rows, cols, frameCount?)
generateSnakeFrames(rows, cols, tailLength?)
generateRippleFrames(rows, cols, options?)

// Static patterns
chevronLeft(rows?, cols?)
chevronRight(rows?, cols?)
vu(cols, levels)
empty(rows, cols)

// Convenience aliases
wave(rows, cols)     // → generateWaveFrames
loader(rows, cols)   // → generateLoaderFrames
pulse(rows, cols)    // → generatePulseFrames
snake(rows, cols)    // → generateSnakeFrames
ripple(rows, cols)   // → generateRippleFrames

Performance

  • Compiled Frames - Frame data is flattened once per prop change before hitting the hot path
  • UI Thread Driver - Animation stepping stays on the UI thread via Reanimated/worklets
  • Skia Picture Path - The shipping renderer keeps geometry stable and redraws the active foreground on the UI thread
  • Lazy SVG Compatibility Path - renderer="svg" stays available for parity checks without affecting Skia startup

Web Support

This library works on web via react-native-web. The default Skia renderer requires CanvasKit on web. For Expo web:

// app.json
{
  "expo": {
    "web": {
      "bundler": "metro"
    }
  }
}

Demo Generation

Generate the GIF/WebP animations seen in this README:

npm run generate:demos -w react-native-dotgrid

This creates optimized GIF and WebP animations in the demos/ directory using the actual frame data from the presets. You can generate specific formats using --gif or --webp flags.

Acknowledgments

This library is inspired by the Matrix component from ElevenLabs UI, reimagined for React Native with cross-platform support and additional animation capabilities.

Contributing

Contributions are welcome. Use the workspace-root commands above so the library package and Expo example share one dependency graph during development.

License

MIT © Tristan Manchester


Made with ❤️ for the React Native community

About

Animated dot-matrix display component for React Native using Skia, Reanimated, and an optional SVG fallback.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors