About this animation
A dictation interface with a breathing light edge and word feedback, with configurable commands, transcript, colors, and font.
Why this one
The lower edge becomes a soft, traveling violet-to-coral horizon that responds to voice amplitude. Newly dictated words begin at 35% opacity and brighten as they are confirmed; the last line stays softer. Command tiles turn an open-ended microphone into a focused action. The light never forms a frame around the screen.
How it works
- A command tap selects a mode and morphs a measured copy of its tile toward the microphone. Press feedback has its own interruptible spring.
- A Reanimated timeline reveals words and drives a simulated voice envelope on the UI thread. Provide
levelto replace the envelope with a real audio meter. - Skia builds a bottom-anchored wave path, applies a three-color gradient and a 32-point blur, and pulses three microphone rings with the same amplitude.
- Each word animates its own ink opacity. A discrete word-index reaction scrolls overflowing transcripts upward; no React state updates occur per frame.
- Completion lowers the horizon, crossfades the microphone into a check, announces the result, then returns home after 1.1 seconds with the matching command highlighted.
This component visualizes dictation; it does not request microphone access or perform speech recognition. The transcript is a supplied string. Listening automatically confirms after duration; stop() confirms earlier.
Install
npx expo install @shopify/react-native-skia react-native-gesture-handler react-native-reanimated react-native-workletsWrap your app in GestureHandlerRootView. Use the Expo SDK 58 Reanimated/worklets setup. On web, load CanvasKit before mounting this component; the catalog host must lazy-load this Skia piece with its Skia loader. No network images or other assets are required.
Usage
import { useRef } from 'react';
import { EdgeGlowDictation, type EdgeGlowDictationRef } from './edge-glow-dictation';
const dictation = useRef<EdgeGlowDictationRef>(null);
<EdgeGlowDictation
ref={dictation}
commands={[
{ id: 'note', label: 'Write note', icon: 'pen-line', color: '#A855F7' },
]}
transcript={'Remember the launch review\non Friday morning.'}
onCommand={(id) => startYourAudioPipeline(id)}
onDone={(text, id) => saveYourResult(text, id)}
/>
// External controls:
dictation.current?.listen('note');
dictation.current?.stop();A theme and timing variant:
<EdgeGlowDictation
transcript="Capture a new idea."
duration={3600}
glowHeight={0.22}
greeting="Good morning,"
guidance="Give your next idea a voice."
startLabel="Start a note"
level={audioLevel}
theme={{
background: '#FAF7FF', surface: '#FFFFFF', text: '#22182B',
muted: '#70647C', border: '#E5DCEC',
violet: '#9052D8', magenta: '#D53D88', coral: '#DD5266',
}}
/>The host supplies safe-area padding and loads its chosen font. The root fills its parent and measures with onLayout. Content is capped at 520 points; the command grid scrolls in short or landscape containers, while the start control stays available. No images are used.
Props
| Prop | Type | Default | Purpose |
|---|---|---|---|
commands | DictationCommand[] | [] | Mode ID, label, dot color and optional included icon. |
transcript | string | '' | Utterance; newlines preserve intended lines. |
level | number | simulated | External amplitude clamped to 0–1. |
onCommand | (id?: string) => void | — | Listening starts or is interrupted. |
onDone | (text: string, id?: string) => void | — | Utterance confirmed, once per listening session. |
onInteraction | () => void | — | Physical touch; the demo cancels autoplay. |
brand | ReactNode | — | Host-provided tile and wordmark. |
greeting | string | Hello, | Start-screen greeting. |
guidance | string | What would you like to create? | Guidance and unselected-mode title. |
startLabel | string | Tap to start | Input button and accessible label. |
commandsLabel | string | START WITH AN IDEA | Command-grid caption. |
listeningLabel | string | Listening… | Status and start announcement. |
doneLabel | string | Got it | Status and completion announcement. |
stopLabel | string | Finish dictation | Microphone accessible label. |
fontFamily | string | system | All interface copy. |
theme | Partial<EdgeGlowDictationTheme> | dark violet | Background, surface, ink, muted ink, border, violet, magenta, coral. |
duration | number | 4400 | Utterance time in ms, minimum 1200. |
glowHeight | number | 0.26 | Maximum relative horizon height, clamped to 0.08–0.4. |
style | StyleProp<ViewStyle> | — | Root container style. |
testID | string | edge-glow-dictation | Prefix for root, start, stop and command controls. |
listen(commandId?) restarts from the current visual state. Unknown IDs are ignored. stop() is a no-op outside an active session. Timers and animations are canceled on unmount.
Compatibility
Built for Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, worklets 0.13.0, Gesture Handler 3.3 and Skia 2.14.0. Uses modules included in this project's Expo Go stack.
Supports iOS, Android, and web. The demo needs the catalog's Skia-aware lazy registration, which is outside this folder's scope.
Accessibility
Buttons provide roles, labels, activation actions and stable test IDs. Command tiles exceed 48 points, the input is at least 56 points, and the microphone is 64 points. Listening and confirmation are announced; the transcript is exposed as one readable utterance. Reduced motion disables the tile flight and voice oscillation and uses 200 ms fades for entry, word appearance and confirmation. Touching anywhere stops the demo autoplay.
Demo timeline
Home → at 800 ms select Generate image → a three-line creative prompt → at 5200 ms confirm → at 6300 ms return home → repeat at 7600 ms. The horizon appears within the first 1.2 seconds. The kit Nimbus tile uses its cloud glyph and blue-violet gradient; all interaction colors belong to this piece.
Icons: Lucide (ISC). Paths are copied unchanged from the project kit into icons.ts.




