appCN
← All components

Voice Sphere

AI
new

A true-3D particle sphere that breathes when idle and ripples with sound when active. The AI-collection voice indicator.

Preview

Scan with Expo Go to run it live

Install Expo Goon Android or iPhone, scan, and the entire appCN gallery loads — real native motion & gestures, not a web shim.

Open in Expo Go Play Store — coming soon
https://expo.dev/preview/update?message=v1.0.0+initial&updateRuntimeVersion=1.0.0&createdAt=2026-05-28T20%3A07%3A21.247Z&slug=exp&projectId=a2d02caa-be26-436a-acd6-f3007862ba0a&group=3ca7e750-9506-4146-8394-1a16c3a917a8

Install

npx @app-cn/cli@latest add voice-sphere

Recommended. Configures NativeWind + Reanimated and registers @app-cn on first run.

Anatomy

A WebGL `<Canvas>` hosts ~3000 particles spaced along a Fibonacci spiral on a unit sphere. A custom GLSL displacement (3D simplex noise) ripples the equator band, a per-vertex color blend tints primary → accent across the surface, and a depth-aware fragment dims back-facing particles so the silhouette reads as real 3D parallax under rotation. A second additive-blended plane behind the sphere supplies the halo pulse. Built on `@react-three/fiber/native` so the same source renders on Expo native (via `expo-gl`) and Expo-web (native browser WebGL).

The delight detail

Watch the equator under load — there's an inner wave at one frequency riding on top of a slower swell at another, so the band never repeats itself. Combined with the depth-faded silhouette and the gentle wobble on the X axis, the sphere reads alive even at zero amplitude.

Props

NameTypeDefaultDescription
activebooleanfalseListening state. Speeds rotation, boosts wave amplitude, and saturates the color blend.
amplitudenumberLive amplitude 0..1. When provided, drives the wave directly — feed it from a metering source. When omitted, an internal idle/active envelope animates instead.
sizenumber240Sphere diameter in px (the Canvas is square).
density"low" | "medium" | "high""medium"Particle count bucket — `low` ≈ 1500, `medium` ≈ 3000, `high` ≈ 6000. Drop to `low` on lower-end Android if framerate dips.
colors{ from: string; to: string }Override the two gradient endpoints. Defaults to the theme primary + accent. Any CSS color string three.js accepts (`#hex`, `rgb()`, `hsl()`, named).
classNamestringExtra NativeWind classes merged onto the outer container.

Examples

Basic

Toggle active to switch between idle breathing and listening.

const [active, setActive] = React.useState(false);
return (
  <View className="items-center gap-6">
    <VoiceSphere active={active} />
    <Button onPress={() => setActive((a) => !a)}>
      {active ? "Stop" : "Start listening"}
    </Button>
  </View>
);

Driven by mic amplitude

Pipe metering from `expo-av` (or any recorder that reports level) into the `amplitude` prop. The sphere skips its internal envelope and follows your signal.

// In your recorder setup, normalize the level to 0..1 and store it in state.
const [level, setLevel] = React.useState(0);
return <VoiceSphere active amplitude={level} />;

Custom palette

Override the gradient endpoints for a brand-aligned look.

<VoiceSphere
  active
  colors={{ from: "#00E5FF", to: "#FF6FA3" }}
/>

Accessibility

  • Container carries `accessibilityRole="image"` with a label that swaps between "Listening" and "Voice indicator idle" — screen readers announce the state, not the visual.
  • The Canvas itself is decorative; the parent View owns the announce.
  • Honors `useReducedMotion()` — freezes the time uniform, drops amplitude to a static value, and stops auto-rotation. The sphere still reads as a 3D object, just still.
  • No interactive elements inside — pair with a real Pressable (mic toggle) that owns the focus + hit target.