Skip to content
Native Animation
Prism Numeral Insight animation preview

This preview uses fictional demo content. See the props below for the customization options available in this component.

npx nativeanimation add prism-numeral-insight
Glass

Prism Numeral Insight

A sleep check-in with a refracting numeral and word-by-word insight, with configurable hours, insight text, palette, and fonts.

$9 for this animation

Add to your project

Copy the component and its assets into your app with the Native Animation CLI, or paste the agent prompt into Claude Code, Codex or Cursor once the MCP server is connected.

About this animation

A sleep check-in with a refracting numeral and word-by-word insight, with configurable hours, insight text, palette, and fonts.

Why this one

The number becomes a poster, rather than another metric inside a card. The glass samples the actual digit scene, displaces it, and separates RGB channels while you drag. Completing the check-in turns those values into a readable sentence with bright data spans. The composition adds a timestamp lens and a segmented sleep summary.

How it works

  1. Render the twelve hour numerals into a memoized four-by-three image atlas on a raster Skia surface, using two independently loaded font sizes. Each tile is fitted to its measured font metrics and advance width, reserving at least 16 points on either side. This preserves CanvasKit typeface ownership and works without an offscreen GPU context.
  2. A module-level Skia RuntimeEffect samples that image. One shared hour position drives clipping, the outgoing numeral, the incoming numeral, and integer notifications to React.
  3. The prism transforms its sampling coordinates, offsets individual RGB channels, and adds bevel highlights inside its signed-distance shape. The circular timestamp lens refracts the same scene.
  4. Gesture Handler separates the hour pan/tap from the prism drag/tap. Springs are interruptible and specify mass; press feedback uses a separate shared value.
  5. The numeral enters with a 24-point rise and opacity on a critically damped spring. The prism then slides in and starts refracting. Done crossfades the background with separate outgoing and incoming text windows, so the screens never show overlapping text. A separate shared writing progress reveals sentence words and highlighted data spans.
  6. The demo owns its timed sequence and stops every pending step on human input. The reusable component has no autoplay timers.

Install

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

Wrap your application in GestureHandlerRootView. For web, configure Skia's CanvasKit loading before importing this component, using your host's Skia loader. The catalog uses lazy loading with Skia initialization. Fonts are supplied by the host; no font dependency is included in the reusable folder.

Usage

import { PrismNumeralInsight } from './prism-numeral-insight';

<PrismNumeralInsight
  numeralFont={require('./assets/Display-Bold.ttf')}
  insight={[
    { text: 'With ' },
    { text: '8 hours', highlight: true },
    { text: ' of sleep, a calmer morning starts here.' },
  ]}
  onHoursChange={(hours) => saveSleep(hours)}
  onDone={() => finishCheckIn()}
/>

With external controls and a complete theme:

const ref = useRef<PrismNumeralInsightRef>(null);

<PrismNumeralInsight
  ref={ref}
  hours={hours}
  onHoursChange={setHours}
  mood={mood}
  onMoodChange={setMood}
  colorScheme="auto"
  palette={{ accent: '#375CEB', button: '#172A76' }}
  insightPalette={{ phase: '#6AE9A5' }}
  numeralFont={require('./assets/Display-Bold.ttf')}
  displayFontFamily="Display-Bold"
  fontFamily="Interface-Regular"
  monoFontFamily="Figures-Regular"
  refraction={34}
  dispersion={4}
  formatHours={(n) => `With ${n} hours\nof sleep`}
/>

ref.current?.setHours(8);

The host applies safe-area insets. The component measures its own container, limits content width to 480 points, and uses scrollable content when embedded in short or horizontal spaces. It fills its host with flex: 1. Font scaling remains enabled. Supply numeralFont on web: system font matching is guarded, but web does not implement it. The temporary RN numeral fallback does not have refraction until that asset loads.

Props

PropTypeDefaultPurpose
hours / onHoursChangenumber / (n) => void7 / —Optimistic controlled integer hours, 1–12; updates are reported at integer crossings, not each frame.
mood / onMoodChangenumber / (n) => void2 / —Five moods, indexed 0–4.
insightInsightSegment[]Sleep and coffee exampleEach span has text and optional highlight. Include spaces between spans.
onReady() => void—Notifies the host when layout and the numeral atlas are ready; autoplay can start from the beginning after font loading.
onDone() => void—Completion callback.
onInteraction() => void—Human input callback, useful for canceling host autoplay.
headerReactNode—Your brand/header; kept away from the top-right viewer controls.
fontFamilystringSystemBody copy and buttons.
displayFontFamilystringSystemHeadings and fallback numeral.
monoFontFamilystringSystemTimestamp and metadata.
numeralFontSkia DataSourceParam—Local display font asset for the refracted numeral.
colorSchemelight | dark | autolightCheck-in theme; completion defaults to dark.
palette / insightPalettePartial<PrismPalette>Built-in light / darkOverride background, foreground, muted, surface, border, accent, button, buttonText, phase, glassTint.
refraction / dispersionnumber28 / 5Displacement and RGB separation in points.
ambientMotionbooleantrueSlow prism drift; stops on human input.
durationnumber560Staged crossfade duration, milliseconds.
formatHours(n) => stringWith n hours\nof sleepHeadline formatter.
formatHoursAnnouncement(n) => stringn hours of sleepSpoken value formatter.
eyebrowstringMORNING CHECK-INCheck-in label.
adjustmentHintstringSwipe up or down to adjustAdjustment help.
moodPromptstringHow do you feel today?Selection prompt.
moodLabelsstring[]Very low, Low, Okay, Good, GreatFive accessible mood labels.
doneLabel / backLabelstringDone / Back to check-inNavigation buttons.
insightEyebrowstringYOUR MORNING, IN FOCUSInsight label.
bedtimestring6:15 AMTimestamp lens copy.
sleepLabel / sleepScore / sleepStatusstringSleep / 52 / EXCELLENTSummary labels and score.
totalLabel / totalSleepstringTotal sleep / 8 h 15 mTotal sleep metadata.
sleepPhasesnumber[]Seven sample fractionsPhase segment widths; normalized automatically.
hoursLabel / prismLabelstringSleep hours / Move the glass prismAccessible gesture labels.
increaseLabel / decreaseLabelstringIncrease / Decrease sleep hoursAccessible step controls.
style / testIDViewStyle / string— / prism-numeral-insightRoot overrides and stable control IDs.

Ref methods

setHours(n), setMood(n), done(), reset(hours?, mood?) (returns to the check-in and replays its entrance; optional values restore the cycle immediately), and movePrism(offset) (normalized offset from -1 to 1). Programmatic controls do not trigger onInteraction.

Demo timeline

All times start when the measured layout and supplied numeral font are ready. The numeral rises 24 points and fades in with a spring without overshoot, followed by the prism sliding in; the entrance takes approximately 1.2 seconds. Prism movement begins at 1.35 s and changes direction at 1.80 s. Hours change from 7 to 8 at 2.35 s, mood 4 (index 3) is selected at 3.35 s, and Done starts at 4.65 s. The 800 ms background crossfade removes the check-in text before revealing the insight text. The sentence completes at about 6.40 s. The sequence repeats at 9.90 s and replays the entrance. Any touch cancels the sequence. The reusable component still defaults to a 560 ms crossfade.

Compatibility

Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, worklets 0.13.0, Gesture Handler 3.3, Skia 2.14.0. Compatible with the installed Expo Go stack; hosts with different native dependencies need a development build.

The shader deliberately uses makeShaderWithChildren with an image shader, rather than the image-filter RuntimeShader API that is unavailable on web.

Accessibility

All controls have roles, labels, and stable IDs. The hour selector supports increment/decrement screen-reader actions, tap-to-advance, and separate 48-point buttons. Mood changes, completed insights, and settled hour adjustments are announced. Hidden screens are removed from accessibility navigation and touch handling.

Reduced motion freezes the prism, removes numeral travel and prism entrance travel, and uses 200 ms fades. The prism gesture stays stationary in that mode. Decorative Skia content does not duplicate the spoken text.

Icons: Lucide (ISC), copied from the repository kit. The Aurora demo brand uses the kit's sparkles glyph and pink-to-violet tile. Geist 800 is used for the demo poster instead of unavailable 700/900 weights; the reusable component accepts any supplied typeface.