Skip to main content

Examples

Seven complete, tutorial-grade apps built on this library — each one a small, documented codebase exercising a different part of the API. They share the same stack (Expo, NativeWind, React Native Skia) and the same core idea: one worklet renders both the live preview and the export.

git clone <repo> && cd <repo>
npm install && npx expo prebuild && npx expo run:ios # or run:android

Island Studio — a tiny video editor

Island Studio demo

mlecoq/react-native-video-editor — montage of gallery clips (up to 6), SkSL shader transitions between clips, draggable/pinchable text and image overlays with enter/exit animations, looping royalty-free soundtracks, hardware-encoded 1080×1920 export.

What it demonstrates best:

  • Timeline with transition overlap — clips store durations; start times come from an accumulator that overlaps each clip with the previous one, and both live clips are handed to a transition shader (timeline.ts).
  • Transitions as SkSL shaders — adding one is copy-pasting from gl-transitions (transitions.ts).
  • Gestures without React renders — drags write into a shared value merged by the drawer on the UI thread (VideoPreview.tsx).
  • Looping music — chained kind: 'audio' items (Audio guide).

Caption Studio — auto-captions

Caption Studio demo

mlecoq/react-native-auto-captions — burns animated, word-timed captions into any video. Parses Whisper verbose_json (word timestamps), SRT or plain text; four caption styles (word-by-word pop, karaoke color sweep, pill box, minimal); drag/pinch placement; export keeps the original audio. The bundled sample carries a real neural voice-over aligned with its transcript.

What it demonstrates best:

  • Time-driven text — captions are a pure function of (currentTime, transcript, settings) (drawCaptions.ts).
  • Paragraph caching — paragraphs are built once per spoken word state (highlights are styled text runs), never per frame (skiaCache.ts).
  • Passing a video's own audio through the preview and the export (audio: true on the video item).

Wave Studio — a music visualizer

Wave Studio demo

mlecoq/react-native-music-visualizer — flips the pipeline: the composition contains no video items at all, just one audio item. Every pixel is generated from precomputed audio analysis. Three scenes (radial spectrum ring, glowing oscilloscope, audio-reactive SkSL shader clouds), color themes, custom artwork, 1080×1920 export with the track as its soundtrack.

What it demonstrates best:

  • Audio-only compositions — the frames extractor drives the clock and plays the track; frames stays empty (composition.ts).
  • Analysis as data, not DSP — bass/mid/high envelopes, spectrum and waveform are precomputed per frame; the worklet just indexes the JSON (scripts/analyze-track.mjs).
  • Your own music, analyzed on-device — pick any audio file: react-native-audio-api decodes it to PCM and the exact same FFT pass runs on the phone, yielding to the UI under a progress bar — the visualizer can't tell a picked song from a bundled one (analyzeTrack.ts).
  • Audio bands as shader uniforms — the Nebula scene is a full-screen SkSL shader whose warp deepens with the bass (scenes.ts).

Frame Studio — a screen-recording beautifier

Frame Studio demo

mlecoq/react-native-frame-studio — gives any screen recording a product-demo look: wallpaper gradient background, rounded shadowed frame, and cinematic tap-to-plant zooms that ease in, hold and ease back out. The simplest engine of the series — no text, no images, no audio analysis, just animated geometry over one video.

What it demonstrates best:

  • A camera as pure functions — a zoom point plays out as ease → hold → ease; getZoomAt(points, t) returns the active transform and edge clamping keeps the wallpaper from leaking through (zoom.ts).
  • One canvas transform inside a clip — the frame never moves; the recording glides under a clipRRect (drawFrame.ts).
  • Gestures and drawer sharing geometry — the recording's placement is a pure function used by both the worklet and the tap handler (VideoPreview.tsx).

Chroma Studio — a green-screen keyer

Chroma Studio demo

mlecoq/react-native-chroma-key — removes the green screen from a video and composites it over a photo or a looping video, with live keying controls. Ships an animated parrot-on-green sample; the whole effect is one SkSL shader.

What it demonstrates best:

  • A video frame as a child shader — the cover-fitted frame shader is handed to the keying effect via makeShaderWithChildren, which samples it per pixel (drawFrame.ts).
  • Keying in the chroma plane — colors are compared on the Cb/Cr axes of YCbCr, so brightness variations of the backdrop key out together; tolerance/softness/spill are plain uniforms (chromaKey.ts).
  • Live uniforms without React renders — dragging a slider writes a shared value the drawer reads on the next frame; release commits to state (KeySheet.tsx).
  • Looping a background video by scheduling — the same file repeated back-to-back in the composition, the drawer picking the active item (composition.ts).

Privacy Studio — on-device face anonymization

Privacy Studio demo

mlecoq/react-native-privacy-studio — blurs, pixelates or emoji-covers every face in a video. Faces are detected on-device (Apple Vision on iOS, ML Kit on Android — a 40-line local Expo module — on frames pulled with expo-video-thumbnails), and the bundled interview sample ships its face track as JSON — the exact shape the on-device analysis produces.

What it demonstrates best:

  • Feeding ML output into the pipeline — detection runs once when a video is picked (seconds, with a progress bar), producing plain JSON; playback and export just interpolate it (analyzeVideo.ts).
  • A local Expo Module when wrappers get in the way — one JS API, Apple Vision behind it on iOS (simulator-friendly, no ML Kit pod), ML Kit on Android (modules/face-detector).
  • Detections become tracks — a tiny greedy IoU tracker turns flickery per-frame boxes into stable per-person tracks that fade in and out; a pure worklet interpolates them at any playback time (faceTracks.ts).
  • Three effects from one clip — the video shader redrawn through a blur image filter, a three-line pixelate SkSL, or a Twemoji PNG, clipped to each face (drawFrame.ts).

Reframe Studio — landscape interviews into vertical shorts

Reframe Studio demo

mlecoq/react-native-reframe-studio — picks a moment out of a long landscape video, follows the speakers with a virtual camera, transcribes the clip with Whisper on the device and exports a captioned 9:16 short. It puts the face detection of Privacy Studio and the caption engine of Caption Studio behind one pipeline.

What it demonstrates best:

  • Trimming is freestartTime on the composition item tells the decoder where to begin, so a three-hour podcast costs exactly what a 30-second clip costs. Picking the moment before analyzing is what keeps the on-device face pass — and the Whisper pass — proportional to the clip you keep (composition.ts).
  • A picker that listens — the audio track is decoded to a plain loudness envelope (react-native-audio-api): waveform under the filmstrip, released handles snap to the nearest pause so a clip never starts mid-word, and the most talkative windows are offered as one-tap suggested moments (moments.ts).
  • True auto-captions — whisper.cpp transcribes the segment on the phone; token timestamps are glued back into words and grouped into caption-sized phrases, all pure functions (transcribe.ts).
  • A camera path, not camera math — dead zone, eased follow with a speed cap, cuts (never pans) when focus moves to another person, a statue filter for face-shaped things that never move: decisions that read better as a JS loop run once after the analysis. The worklet only interpolates the result, and never across a cut (camera.ts).
  • Two spaces that never meet — the camera works in source space, the captions in output space, drawn after the crop transform is restored; the caption engine doesn't even know the video was cropped (drawFrame.ts).

Patterns shared by all seven

  • One worklet for preview and export — what you see is exactly what you save; there is no second rendering path to keep in sync.
  • Resolution independence — all geometry lives in fractions of the canvas, so the small preview and the full-size export render the same composition.
  • Assets across worklet runtimes — images travel as raw Uint8Arrays and each runtime decodes its own SkImages; the Skia typeface provider is created once on the JS thread and captured by the worklets. Skia objects built inside a runtime (paragraphs, compiled shaders) are cached per runtime on globalThis.
  • Worklet helpers are defined before their callers — closures are captured at the definition site; a helper declared further down the file would be captured uninitialized and crash at runtime.
  • Version matrix — the examples track the combination shipped in production by Azzapp: @shopify/react-native-skia 2.10.x, Reanimated 4.5, skia-video 0.10.