About this animation
A speaking gel orb with a tappable karaoke transcript and configurable text, reading pace, colors, and font.
How it works
- One continuous
spokenshared value tracks the fractional word currently being read. - Each word derives its color and subtle rise from that value, so tapping any word seeks immediately.
- Words with at least seven letters launch directional traveling waves around the rim, each with one damped rebound (mass 1, stiffness 64, damping 12; damping ratio 0.75). An optional audio level adds slow deformation.
- One compact Canvas combines the violet-to-cyan gel shader, moving caustic filaments, two sliding specular highlights, a radial aura, and up to four translucent bubbles emitted by long words, each rising over 2.6 seconds.
- A falling droplet flattens and grows with one subdued rebound; an expanding ambient wash and delayed transcript reveal finish within 1.8 seconds. Each spoken word leaves cyan ink that fades continuously over 800 ms.
- Reduced motion skips the introduction and freezes the gel, removing impacts, particles, and text glow while retaining readable state changes.
Install
npx expo install @shopify/react-native-skia expo-linear-gradient react-native-reanimated react-native-workletsFor web, initialize CanvasKit before mounting the component using the application's Skia loader. No native package beyond these Expo SDK compatible dependencies is required.
Usage
import JellyOrbTranscript from './JellyOrbTranscript';
<JellyOrbTranscript text={'A short line.\nA second line.'} />import { useRef } from 'react';
import { useSharedValue } from 'react-native-reanimated';
import JellyOrbTranscript, { type JellyOrbTranscriptRef } from './JellyOrbTranscript';
const level = useSharedValue(0);
const player = useRef<JellyOrbTranscriptRef>(null);
<JellyOrbTranscript ref={player} level={level} wordsPerSecond={2.7} onOpenChat={openChat} />;
player.current?.seekToWord(4);Props
| Prop | Type | Default | Description |
|---|---|---|---|
text | string | Design-review joke | Transcript; line breaks are preserved. |
wordsPerSecond | number | 2.7 | Word cadence; adds 600 ms after commas, colons, and semicolons, and 1.2 s after sentences. |
colors | JellyOrbTranscriptColors | Navy, white, muted violet, and violet-blue-cyan orb | Full background and text palette. |
level | SharedValue<number> | undefined | Optional normalized voice level for orb deformation. |
onToggle | (playing: boolean) => void | undefined | Playback state callback. |
onOpenChat | () => void | undefined | Chat button callback. |
onBack | () => void | undefined | Back button callback. |
fontFamily | string | System font | Optional transcript font. |
style | StyleProp<ViewStyle> | undefined | Container style. |
testID | string | undefined | Root test identifier. |
The ref exposes play(), pause(), restart(), and seekToWord(index).
Compatibility
Targets Expo SDK 58, React Native 0.88, Reanimated 4.7, and Skia 2.14. Intended for iOS, Android, and web in Expo Go with the matching SDK.
Accessibility
Interactive controls have button roles and labels. Each word is an accessible seek target. Reduced motion removes the introduction, moving shader, traveling waves, bubbles, and text glow while preserving the spoken and pending color distinction. The host screen applies safe-area insets.
Why this one
The transcript and orb share one reading position, turning static speech copy into a seekable voice moment with tactile visual feedback.
Icons: Lucide (ISC).
Calm motion tuning
Timing constants are grouped at the top of JellyOrbTranscript.tsx. The public props and ref methods are unchanged.
The pre-adjustment rush came from these locations in that file (line numbers refer to ticket 1):
| Source | Before | After |
|---|---|---|
| Playback, lines 364–384 and 461–492 | 3.2 words/s; only em dashes paused | 2.7 words/s; comma pause 600 ms, sentence pause 1.2 s |
| Impact dispatch, lines 471–485 | Every word | Only words with at least seven letters |
| Gel waves, lines 70–80 | 20 rad/s; amplitude 0.067; propagation 8 rad/s | 5.29 rad/s; amplitude 0.03; propagation 2.5 rad/s; one rebound, damping ratio 0.75 |
| Internal flow, lines 79–101 | Full-speed noise and specular movement | Quarter-speed noise, specular, and idle rim motion; six-second breathing cycle |
| Bubbles, lines 198–214 and 313 | Up to 12; 1.25 s rise | Up to four; 2.6 s rise |
| Word ink, lines 730–750 | Exponential fade (263 ms time constant); active row glow switched abruptly | 80 ms soft onset and continuous fade ending at 800 ms; no abrupt row glow |
| Introduction, lines 57–60 and 447–448 | 1.4 s; multiple stronger rebounds | 1.8 s; smaller single damped rebound |
Pauses begin after a punctuation-bearing word finishes. Seeking clears any pending pause. Reduced motion retains the same reading cadence without animated gel or glow.




