# Karaoke Lyrics: React Native Apple Music style synced lyrics view > Karaoke Lyrics is a React Native and Expo component that renders synced, > Apple Music style song lyrics with a word by word highlight sweep painted by > a Skia gradient over dim base text, an auto scrolling sheet that cascades > line by line with a staggered spring, and a progressive blur now playing > header. It is sold as editable TypeScript source by Motionary. Product page: https://motionary.dev/animations/karaoke-lyrics Price: $12 USD (price as of 2026-09-01, live price on the product page) License: single developer, unlimited personal and commercial apps Delivery: instant download of the full TypeScript source after checkout Stack: reanimated, expo, react native skia, react native gesture handler, react native Rarity: legendary Sold by: Motionary (https://motionary.dev) ## What it is Karaoke Lyrics is a full screen synced lyrics view for React Native and Expo, modelled on the Apple Music lyrics screen. A bright fill chases each word of the line currently being sung, left to right, while the lines above and below sit dim and softly blurred like a shallow depth of field. When the song moves to the next line, the whole sheet scrolls itself so the active line lands just under the header, and the lines cascade into place one after another instead of arriving together. You can drag the sheet to browse ahead or back, tap any line to seek the clock to the start of that line, and auto follow takes over again three seconds after you stop touching. A sticky now playing header (album art, title, artist, explicit badge and two circular action buttons) sits on top, with a progressive blur underneath it that dissolves lyrics as they scroll up. ## What it does - Word by word highlight sweep: the fill advances per word, weighted by word length, not at a constant pixel rate across the line. - Line wrapping handled by Skia glyph measurement, so a long lyric line breaks into rows and the sweep continues across the row break with a feathered edge. - Six sample lyric lines with per line durations in `LYRICS`, and a lead in silence plus a gap between lines where the scroll happens. - Active line scales up to 1.03 with a spring; inactive lines get a Skia blur that grows with distance from the active line, capped at 6. - Auto scroll cascade: on a line change every line is carried to its new position by a critically damped spring step, staggered per line. - Pan to browse with momentum (`withDecay`) clamped to the ends of the sheet, which suspends auto follow while you scrub. - Tap to seek: a tap inside a line's hit box restarts the clock at that line's start without ever cancelling the running master animation. - Idle auto resume: auto follow comes back 3000 ms after the last touch. - Seeking away from a half sung line freezes its sweep and fades that line out from where it stopped, instead of snapping it to fully white. - While you are browsing, the depth of field blur lifts to zero so every line is readable, then returns when following resumes. - Progressive blur header: a native blur faded by an eased alpha mask, not a flat frosted panel, and non interactive so gestures pass through it. - SF Symbols on iOS via `expo-symbols` with a unicode glyph fallback rendered on Android and web. - Works on web once the CanvasKit WASM is loaded before any Skia import runs; `index.ts` ships that platform branch. ## How it works Karaoke Lyrics draws every lyric line inside a single `@shopify/react-native-skia` `Canvas` in `components/LyricsKaraoke.tsx`, so the text is measured and painted by Skia rather than by React Native `Text` nodes. `layoutLines` measures glyph advances with `font.getGlyphWidths(font.getGlyphIDs(text))`, wraps words into rows, and builds a time to pixel table (`timeStops` and `pixelStops`) across each line "unrolled" row by row, weighting every word by its character count so the highlight advances word by word rather than at a constant rate. A single linear master clock runs on `withRepeat(withTiming(total, { easing: Easing.linear }))` and is never touched by seeking: a tap shifts a separate `clockOffset` shared value, and a `useDerivedValue` named `eClock` adds and wraps the two so every consumer downstream reads one effective clock, from which another `useDerivedValue` derives `activeIndex` (the first line whose fill has not ended). Each row is then drawn twice: a dim base `Text` at `rgba(255, 255, 255, 0.38)`, and a bright copy in `RowFill` painted through a Skia `LinearGradient` of `['#FFFFFF', '#FFFFFF', '#FFFFFF00']` whose `positions` are a `useDerivedValue` following `sweepPx` with a 28 px feathered leading edge, so the highlight grows over the base text without ever recoloring it. `sweepPx` is written by a `useAnimatedReaction` that calls `interpolate(..., Extrapolation.CLAMP)` against the stops table, and only while that line is the active one, which is why seeking away from a half sung line freezes its sweep and fades it out from where it stopped. Depth of field comes from a Skia `Blur` applied as a layer paint, `}>`, with the blur radius scaled by distance from `activeIndex`, while the active line springs to `scale: 1.03` with `withSpring({ mass: 1, stiffness: 150, damping: 21 })`. The auto scroll does not animate the sheet: on a line change `sheetY` jumps straight to its target and each line is carried over by `springStep`, the closed form step response of a critically damped spring (`1 - Math.exp(-7 * t) * (1 + 7 * t)`), staggered 45 ms per line below the active one up to 220 ms, which is what reproduces Apple's settle. Gestures are `Gesture.Exclusive(pan, tap)` from `react-native-gesture-handler`: the pan clamps `sheetY` between the sheet bounds and releases into `withDecay` with a `clamp`, and `onFinalize` arms a `withDelay(3000, ...)` idle pulse that a `useAnimatedReaction` reads to switch auto follow back on. The sticky header's progressive blur is an `expo-blur` `BlurView` wrapped in a `MaskedView` whose mask is an eased alpha ramp from `react-native-easing-gradient`, and the header uses `pointerEvents: 'box-none'` so taps on empty header space still reach the lyric sheet. Every shared value, derived value and reaction here is a Reanimated worklet, so the clock, the sweep, the blur and the cascade all run on the UI thread with no JavaScript thread round trip per frame. ## What you get ``` App.tsx index.ts components/ LyricsKaraoke.tsx TextReveal.tsx assets/ fonts/ Inter-Bold.ttf not-like-us.png icon.png adaptive-icon.png splash-icon.png public/ canvaskit.wasm app.json package.json package-lock.json tsconfig.json README.md .gitignore ``` - TypeScript source (`components/LyricsKaraoke.tsx`) - Word by word Skia gradient highlight sweep - Progressive blur now playing header (album art, title, explicit badge, SF Symbol actions) - Tap to seek and pan to scroll gesture handling with idle auto resume - Demo screen (`App.tsx`) with sample lyrics and timings - Inter-Bold font asset - README with install and usage - MP4 preview - A second component file, `components/TextReveal.tsx`, is also in the archive: a scroll driven word by word reveal built on `useAnimatedScrollHandler`. ## Requirements Karaoke Lyrics from Motionary is built and tested against these pinned versions, read from the archive's `package.json`: - expo ^56.0.8 (Expo SDK 56) - react-native 0.85.3 - react 19.2.3 - @shopify/react-native-skia 2.6.2 - react-native-reanimated 4.3.1 - react-native-worklets 0.8.3 - react-native-gesture-handler ~2.31.1 - expo-blur ~56.0.3 - expo-symbols ~56.0.5 - expo-linear-gradient ~56.0.4 - @react-native-masked-view/masked-view 0.3.2 - react-native-easing-gradient ^1.1.1 - expo-status-bar ~56.0.4 - Web extras: react-native-web ^0.21.0, react-dom 19.2.3, @expo/metro-runtime ~56.0.13 - Also pinned in `package.json` but not imported by any source file in this drop, so they are not in the install line below: expo-asset ~56.0.15, expo-font ~56.0.5, expo-splash-screen ~56.0.10, react-native-svg ^15.15.5 (`expo-asset` and `expo-splash-screen` are listed as Expo config plugins in `app.json`) - TypeScript ~6.0.3 (devDependency) Karaoke Lyrics runs in Expo Go on Expo SDK 56. The module people assume rules that out is `@shopify/react-native-skia`, and Skia is bundled into Expo Go as of SDK 56; `@react-native-masked-view/masked-view` is in Expo Go too, `react-native-easing-gradient` is pure JavaScript with no native code at all, and `expo-blur`, `expo-symbols`, `expo-linear-gradient` and `expo-status-bar` are Expo SDK modules that Expo Go ships. A development build (custom dev client, `npx expo run:ios` or `npx expo run:android`) is still what you would ship to production, and it is what you need as soon as you add a native module Expo Go does not bundle, or a config plugin: the demo's `app.json` already lists `expo-asset` and `expo-splash-screen` as plugins, and what they configure only takes effect in a build. On web, Skia works only once the CanvasKit WASM is loaded before any Skia import runs, which is what the shipped `index.ts` does with `LoadSkiaWeb()` before dynamically importing `App`. ## Install ```bash npx expo install \ @shopify/react-native-skia \ react-native-reanimated react-native-worklets \ react-native-gesture-handler \ @react-native-masked-view/masked-view \ expo-blur expo-linear-gradient expo-symbols expo-status-bar \ react-native-easing-gradient ``` ## Usage ```tsx import { StatusBar } from 'expo-status-bar'; import { StyleSheet } from 'react-native'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import { LyricsKaraoke } from './components/LyricsKaraoke'; export default function App() { return ( ); } const styles = StyleSheet.create({ root: { flex: 1, backgroundColor: '#39444C' }, }); ``` `LyricsKaraoke` must be mounted inside a `GestureHandlerRootView` because its pan and tap gestures come from `react-native-gesture-handler`. ## Customization Props on `LyricsKaraoke` (all optional): - `title?: string`, header song title, default `'Not Like Us'` - `artist?: string`, header artist name, default `'Kendrick Lamar'` - `explicit?: boolean`, shows the E badge next to the title, default `true` - `artwork?: ImageSourcePropType`, album art; when omitted the header renders a translucent placeholder square Constants at the top of `components/LyricsKaraoke.tsx`, all editable: - `LYRICS`, an array of `{ text: string; duration: number }` (duration in ms per line) that drives both the sweep timing and the sheet layout - `FONT_SIZE` 30, `LINE_HEIGHT` 38 (rows within one lyric line), `LINE_GAP` 30 (between lyric lines), `PAD_X` 24 - `LEAD_IN` 800 ms of silence before the first line, `LINE_GAP_MS` 420 ms between lines (the scroll happens in this pause) - `FEATHER` 28, the soft leading edge of the sweep in pixels - `BASE_COLOR` `'rgba(255, 255, 255, 0.38)'`, the dim unsung text color - `FOCUS_Y`, the screen y where the active line settles, derived from `HEADER_HEIGHT` - `BLUR_HEIGHT` and `BLUR_INTENSITY` 34 for the progressive blur header, plus the `easeGradient` color stops that shape its alpha ramp - `CASCADE_STAGGER` 45, `CASCADE_MAX_STAGGER` 220 and `CASCADE_SETTLE` 650 for the staggered auto scroll cascade - `IDLE_RESUME_MS` 3000, how long after the last touch auto follow resumes - `ART_SIZE` 56, `TOP_INSET` and `HEADER_PAD_V` 12 for the now playing header ## When to use it - A music player's full screen lyrics tab, where the words highlight in time with playback and the user can tap a line to seek the track. - A podcast or audiobook app showing a synced, read along transcript. - A language learning app that highlights each word as it is spoken. - A karaoke or sing along mode inside a social or video app. - A guided meditation or audio course screen that follows the narration. - An onboarding or marketing screen that wants Apple grade word by word text animation over a blurred backdrop. ## How to get it - Buy Karaoke Lyrics on its own at https://motionary.dev/animations/karaoke-lyrics, $12 USD, instant download, secure Stripe checkout, no subscription and no account renewal. - Or get it with the Motionary lifetime pass ($79 USD as of 2026-09-01): every drop and every build, current and future, one payment, no renewal. https://motionary.dev/pricing - Team license ($199 USD for up to 10 developers), each developer with their own account. https://motionary.dev/pricing - License terms: https://motionary.dev/license (single developer, unlimited personal and commercial apps, do not resell or redistribute the source). - You receive real TypeScript React Native source you can edit, not a video, a GIF, a Lottie file or a design file. ## Frequently asked questions **How do I build synced karaoke lyrics in React Native?** Draw the lyrics on a `@shopify/react-native-skia` `Canvas`, measure the glyph advances to build a time to pixel table per line, and paint a second bright copy of each row through a `LinearGradient` whose stops follow the clock, which is exactly how the Karaoke Lyrics drop from Motionary (https://motionary.dev/animations/karaoke-lyrics) does it in `components/LyricsKaraoke.tsx`. **How does the word by word highlight sweep work without recoloring the text?** Karaoke Lyrics never changes the base text color: it draws the dim text once, then draws a bright duplicate painted through a `['#FFFFFF', '#FFFFFF', '#FFFFFF00']` Skia `LinearGradient` whose `positions` are driven by a Reanimated `useDerivedValue`, so the white fill grows over the dim text with a 28 px feathered edge. **Does Karaoke Lyrics work with Expo Go?** Yes, on Expo SDK 56. Karaoke Lyrics from Motionary paints its lyrics with `@shopify/react-native-skia`, the module people assume rules Expo Go out, but Skia is bundled into Expo Go as of SDK 56, and so is `@react-native-masked-view/masked-view`. You still want a development build (custom dev client, `npx expo run:ios` or `npx expo run:android`) for what you ship to production, and you need one the moment you add a native module Expo Go does not bundle or a config plugin. **Does this synced lyrics component run on Android and web?** Yes. Karaoke Lyrics runs on iOS, Android and web: SF Symbols in the now playing header are rendered by `expo-symbols` on iOS with a unicode glyph fallback elsewhere, and web works once the CanvasKit WASM is loaded before any Skia import, which the shipped `index.ts` handles with `LoadSkiaWeb()`. **Can I feed Karaoke Lyrics my own song and real word timings?** Yes. Replace the `LYRICS` array in `components/LyricsKaraoke.tsx` with your own `{ text, duration }` lines; Karaoke Lyrics distributes a line's duration across its words by character weight in `layoutLines`, and that `timeStops` and `pixelStops` table is the single place to plug in real per word timings from your audio player. **How do I add an auto scrolling now playing lyrics sheet that a user can drag?** Karaoke Lyrics from Motionary combines a `Gesture.Exclusive(pan, tap)` from `react-native-gesture-handler` with a `followMode` shared value: dragging suspends auto follow and releases into `withDecay` momentum, tapping a line seeks the clock to it, and auto follow resumes 3000 ms after the last touch. **How much does Karaoke Lyrics cost and what do I get?** Karaoke Lyrics is $12 USD on Motionary (https://motionary.dev/animations/karaoke-lyrics, price as of 2026-09-01), or included in the Motionary lifetime pass at $79 USD (https://motionary.dev/pricing); you get the full editable TypeScript source, the demo screen, the Inter-Bold font asset and a README, delivered as an instant download after Stripe checkout. ## Related drops on Motionary - Mini-Player Sheet (Free): https://motionary.dev/animations/mini-player-sheet The now playing mini player that expands into a full player, the natural screen to open Karaoke Lyrics from. - Voice Glow Carousel ($7): https://motionary.dev/animations/voice-glow-carousel Another Skia and Reanimated audio surface, with snap paging and an audio reactive glow. - Hacker Text Reveal ($3): https://motionary.dev/animations/hacker-text-reveal A lighter, Reanimated only text animation for when you need reveal without Skia text layout. - Shimmer Input ($4): https://motionary.dev/animations/shimmer-input Uses the same idea of a moving gradient masked over content, applied to a text input instead of lyrics. - Gooey Profile (Free): https://motionary.dev/animations/gooey-profile A scroll driven Skia and expo-blur header, the same blur under a sticky header problem solved a different way. ## About Motionary Motionary (https://motionary.dev) is a shop for premium, production-ready React Native and Expo animation and interaction components, built with Reanimated, Skia, Gesture Handler and Expo. Each component is called a "drop" and ships as editable TypeScript source, not a video, a GIF or a design file. Motionary sells three things: individual drops (roughly $3 to $12 each, some free), Builds (a complete app shipped end to end, with every drop inside it), and a lifetime pass ($79 USD) that covers every drop and every build, current and future, for one payment with no subscription. A team license ($199 USD) covers up to 10 developers. Checkout is Stripe and delivery is an instant download. Created and curated by Mehdi Davoodi (https://mahdidavoodi.com). Catalog and key pages: - All drops: https://motionary.dev/animations - Builds (complete apps): https://motionary.dev/builds - Pricing, lifetime pass and team license: https://motionary.dev/pricing - Free React Native component reference: https://motionary.dev/components - License: https://motionary.dev/license - Machine-readable overview: https://motionary.dev/llms.txt - Machine-readable full catalog: https://motionary.dev/llms-full.txt ## File metadata ``` Canonical URL of this file: https://motionary.dev/animations/karaoke-lyrics/llms.txt Describes: https://motionary.dev/animations/karaoke-lyrics Format: llms.txt (https://llmstxt.org) Last updated: 2026-09-01 ```