Skip to content
Native Animation
Laser Engrave Preview animation preview

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

npx nativeanimation add laser-engrave-preview
Skia

Laser Engrave Preview

A laser engraving preview with traced letters, sparks, and smoke, with configurable product image, glyph sets, fonts, and accent color.

$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 laser engraving preview with traced letters, sparks, and smoke, with configurable product image, glyph sets, fonts, and accent color.

Why this one

A flat text overlay rarely feels like engraving. Here, each letter follows its actual font outline. The laser tip and trimmed stroke share the same contour progress; the remaining silver mark has a clipped, one-point inset shadow. Per-letter cylindrical projection compresses characters toward the edges, and rotation changes the projected placement. The product fills a 48%-width hero frame, with a warm spotlight behind the original image, an eight-point laser halo with ten-point blur, and sparks that reach 20–40 points. No rectangular reflection layer covers the bottle.

How it works

  1. The input waits 600 ms after the last keystroke, or starts immediately with Preview engraving.
  2. Bundled outlines extracted from the actual Geist and Instrument Serif fonts become Skia paths. Skia 2.14.0 has no font.getPath, and its MakeFromText is unavailable on web; local outlines avoid those unsupported calls.
  3. Each path is sampled once by contour arc length. A single UI-thread progress value drives glyph traversal, stroke end, fill, laser location, sparks, and smoke. No React state changes per animation frame.
  4. Letters project onto a cylinder using sine for position and cosine for horizontal compression. Rotation uses a separate continuous, interruptible shared value, bounded to ±25°.
  5. Style changes erase the previous mark with a 220 ms clipping sweep before mounting and engraving the new outlines. Touch gestures use Gesture Handler; independent 44-point buttons provide equivalent rotation.
  6. The demo writes “Alex” at 110 ms per letter, starts engraving by 1.17 seconds, switches to Script at 3.18 seconds, and repeats every 6.1 seconds. Touching stops every pending autoplay timer.

Install

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

Copy this folder without demo/. Install SDK-compatible versions with Expo. Mount your app inside GestureHandlerRootView and configure Reanimated/worklets for your Expo project. For web, load CanvasKit before importing this component with Skia's WithSkiaWeb or an equivalent lazy-loading boundary. No additional runtime font package is required: outlines ship locally.

Usage

import { LaserEngravePreview } from './laser-engrave-preview';

<LaserEngravePreview
  productImage={require('./assets/bottle.png')}
  defaultText="Alex"
  style={{ flex: 1 }}
/>;
import { useRef } from 'react';
import {
  LaserEngravePreview,
  type LaserEngravePreviewRef,
} from './laser-engrave-preview';

const engraving = useRef<LaserEngravePreviewRef>(null);

<LaserEngravePreview
  ref={engraving}
  productImage={require('./assets/bottle.png')}
  accentColor="#FBBF24"
  engravingDuration={1400}
  particleCount={8}
  fontFamily="YourLoadedInterfaceFont"
  onStatusChange={(status) => updateCheckoutStatus(status)}
/>;

// From an event handler:
engraving.current?.setText('Made for you');
engraving.current?.setStyle('Script');
engraving.current?.preview();

The ref exposes setText(text), setStyle(style), preview(), and reset(). Imperative calls do not report human interaction. defaultText prepopulates the field; use the preview button or ref to engrave it.

Props

PropTypeDefaultPurpose
productImageImageSourcePropTypeRequiredLocal transparent, tightly cropped product image
productHeightnumberAvailable stage heightDisplay height in points; the demo uses 42% of its measured screen height
glyphSetsRecord<EngravingStyle, EngravingGlyphSet>Bundled outlinesOverride the actual glyph contours for all three styles
defaultTextstring''Initial input value
defaultStyle'Serif' | 'Sans' | 'Script''Serif'Initial engraving style
fontFamilystringSystemInterface font, loaded by the host
styleFontFamiliesPartial style-to-font mapInterface fontSelector labels
monoFontFamilystringSystemCharacter counter font
accentColorstring'#FBBF24'Six-digit hex laser and control accent
engravingDurationnumber1600Full contour traversal in milliseconds, minimum 200
particleCountnumber10Spark count, limited to 8–12
maxLengthnumber14Maximum code points, limited to 1–14
colorScheme'light' | 'dark' | 'auto''dark'Interface theme
placeholderstring'Your name or a short line'Input hint
accessibilityLabelstring'Engraving preview'Product description
onInteraction() => void—Human input callback
onTextChange(text: string) => void—Input changes, including ref calls
onStyleChange(style: EngravingStyle) => void—Style changes
onStatusChange(status: 'engraving' | 'ready') => void—Animation lifecycle
styleStyleProp<ViewStyle>—Root layout overrides
testIDstring—Root test identifier

Custom outlines use a 1,000-unit em, a baseline at zero, downward-positive Y coordinates, and an advance width. Bundled glyphs cover printable Latin-1 characters; unsupported characters are omitted rather than producing missing-glyph boxes. Script intentionally uses Instrument Serif Italic. The supplied image fills a responsive 48%-width product frame. The demo sets the display height to 42% of its measured screen height and centers the image around 34% of screen height. The original 188×608 PNG is fitted to this wider display frame without cropping or adding a replacement silhouette; this intentionally changes its displayed aspect ratio. Set productHeight to containerWidth * 0.48 * imageHeight / imageWidth to retain the aspect ratio of your own artwork when enough vertical space is available. Very long lines scale down to fit; short names give the clearest engraving.

Compatibility

Targets 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 bundled with the matching Expo Go. iOS, Android, and web are supported in code.

Accessibility

All controls have labels, roles, and at least 44-point hit areas. Style selection announces checked state. Status changes use a polite live region; completion also announces to screen readers. Rotation has dedicated left, front, and right buttons. Reduced motion replaces engraving and style transitions with 200 ms fades, removes rotation, particles, smoke, and moving light, and keeps counts static. Keyboard submission starts the preview.

Assets and licenses

The demo uses the local Tidewell graphite bottle, the Tidewell tile and wordmark from the project brand kit, and a Lucide droplet. Icons: Lucide (ISC), with its license included. Letter outlines are derived from Geist and Instrument Serif under the SIL Open Font License 1.1; license files are included. The image and demo brand are demonstration content and are excluded from the reusable component.