Files
poyraz-portfolio/content/animation-sources/poyraz-cursor-portrait.en.md
T

880 lines
37 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "How to Build an AI Avatar That Follows the Pointer"
slug: "poyraz-cursor-portrait"
excerpt: "Build the effect from start to finish with the prompts, Wiro AI and MiniMax H3 video workflow, FFmpeg preparation, and React integration I used."
coverImage: "/animation-sources/poyraz-cursor-portrait/avatar-mouse-follow.gif"
platform: "Web"
tools:
- "Wiro AI"
- "MiniMax H3"
- "FFmpeg"
- "React"
date: "2026-08-29"
author: "Poyraz Avsever"
lang: "en"
---
The portrait at the bottom-right is not playing like a normal video. It remains paused while the pointer's vertical position controls the video timeline. At the bottom of the screen the portrait looks down-left, in the middle it looks horizontally left, and at the top it looks up-left.
I generated the video with the **MiniMax H3** model through **Wiro AI** in a **1:1 square format**. I then prepared it for frequent seeking with FFmpeg and connected its `currentTime` to the pointer's Y position in React.
Every prompt on this page is copyable and can be adapted to your portrait, avatar, or brand character.
## How to use the `[[...]]` fields
Double square brackets mark values you must replace. Do not leave `[[OUTFIT]]` in the final prompt; replace it with a concrete value such as `plain red polo shirt`.
| Variable | What it means | Value in this project |
| --- | --- | --- |
| `[[SUBJECT]]` | Person or character | young male software creator |
| `[[OUTFIT]]` | Clothing | plain red polo shirt |
| `[[BACKGROUND_COLOR]]` | Flat background | pure white, `#FFFFFF` |
| `[[EXPRESSION]]` | Fixed expression | calm, natural, neutral |
| `[[ASPECT_RATIO]]` | Generation ratio | `1:1` |
| `[[HEAD_DIRECTION]]` | Fixed horizontal angle | about 60 degrees left |
| `[[VIDEO_PATH]]` | Public video path | `/media/cursor-portrait/poyraz-bottom-right.mp4` |
| `[[POSTER_PATH]]` | Public poster path | `/media/cursor-portrait/poyraz-bottom-right-poster.webp` |
| `[[FRAMEWORK]]` | Application stack | Next.js, React, TypeScript |
| `[[STYLING_SYSTEM]]` | Styling stack | Tailwind CSS |
Search for every `[[...]]` field before submitting a prompt and make sure no unresolved variable remains.
## How the effect works
The reliable way to control a single video in real time is to treat it as a short **motion-control plate**, not as an autoplaying clip.
The four-second timeline in this project:
1. `0.000.25`: hold the down-left pose.
2. `0.253.75`: move from down-left to up-left.
3. Around `2.00`: reach the neutral horizontal-left pose.
4. `3.754.00`: hold the up-left pose.
Moving the pointer vertically scrubs this active range forward or backward. Pointer X is intentionally ignored because the generated video contains only one controlled motion axis.
> A single video is reliable only along the motion axis it contains. For true horizontal and vertical tracking, use a consistent 3×3 set of directional stills instead of inventing a second axis in code.
## Production workflow
1. Select a clear, front-facing identity reference.
2. Prepare a consistent 1:1 master frame with fixed clothing, light, and background.
3. Upload the master frame to Wiro AI and generate the motion with MiniMax H3.
4. Regenerate with the repair prompt if the face, camera, or background drifts.
5. Convert the result into a seek-friendly 720×720 H.264 web asset.
6. Map pointer Y to the video's active time range.
7. Test desktop, reduced-motion, dark-theme, and mobile behavior separately.
## 1. Master frame prompt
Upload a clear identity reference to your image-generation tool and replace every `[[...]]` field first.
```prompt
Use the uploaded image only as the identity reference for [[SUBJECT]].
Create a new photorealistic, production-ready 1:1 studio portrait for an
interactive website animation. Preserve the exact recognizable identity,
facial proportions, skin tone, hairstyle, hairline, eyebrows, eye shape,
nose, lips, jawline, age, and overall appearance.
Composition:
- Square [[ASPECT_RATIO]] frame.
- Medium close-up from [[CROP_POINT]] upward.
- Keep the full head, hair, ears, neck, shoulders, and visible upper torso
safely inside the frame.
- Keep comfortable negative space around the hair and shoulders.
- The shoulders remain stable and the head is turned approximately
[[HEAD_DIRECTION]].
- Expression: [[EXPRESSION]].
- Outfit: [[OUTFIT]].
Background and light:
- Perfectly flat, seamless [[BACKGROUND_COLOR]] background.
- No gradient, texture, horizon line, furniture, props, text, watermark,
logo, border, or visible cast shadow.
- Soft, bright studio lighting with natural skin texture.
- Keep hair, ears, face, shoulders, and clothing edges clean.
Continuity constraints:
- Do not beautify, age, de-age, stylize, or reinterpret the person.
- Do not change facial hair, outfit, accessories, body proportions, or light.
- Do not crop the hair, ears, shoulders, or upper torso.
- Generate one person and one clean master frame only.
```
Values used for this implementation:
```text
[[SUBJECT]] = a young male software creator
[[ASPECT_RATIO]] = 1:1
[[CROP_POINT]] = mid-torso
[[HEAD_DIRECTION]] = 60 degrees toward screen-left
[[EXPRESSION]] = calm, natural, neutral expression
[[OUTFIT]] = plain red polo shirt
[[BACKGROUND_COLOR]] = pure white (#FFFFFF)
```
## 2. Wiro AI / MiniMax H3 video prompt
Use the master frame as the image reference in Wiro AI with the MiniMax H3 model. The goal is a technical plate that works frame by frame, not a cinematic scene.
```prompt
Animate the uploaded 1:1 master frame into a precise four-second motion-control
plate for an interactive website portrait. Preserve the exact identity, face,
hairstyle, red polo shirt, body proportions, lighting, colors, square framing,
and pure white background from the reference image.
Output:
- Duration: exactly 4.0 seconds.
- Aspect ratio: 1:1.
- One continuous shot with a completely locked, eye-level camera.
- No zoom, crop change, pan, tilt, dolly, reframing, or camera shake.
- No speech and no audio-dependent movement.
Head direction:
- Keep the subject turned approximately 60 degrees toward screen-left for
the entire video.
- The horizontal head angle must not change.
- Never turn toward the camera and never rotate into a full side profile.
Exact motion timeline:
- 0.000.25 seconds: hold a clean down-left gaze and head-tilt pose.
- 0.253.75 seconds: move smoothly and continuously from down-left to up-left.
- At exactly 2.00 seconds: reach a neutral horizontal-left gaze.
- 3.754.00 seconds: hold the final up-left pose perfectly still.
Movement rules:
- Only the eyes and the minimum natural head/neck tilt required for the
vertical gaze may move.
- Shoulders, torso, arms, clothing, body position, head scale, and horizontal
head angle remain fixed.
- Keep the mouth closed and motionless.
- No talking, smiling, eyebrow movement, nodding, leaning, body sway,
breathing motion, or secondary gesture.
- Movement must be slow, linear, anatomically coherent, and usable when
scrubbed both forward and backward.
Continuity:
- Preserve the same recognizable face in every frame.
- Keep hair volume, hairline, ears, nose, jaw, skin texture, clothing folds,
and lighting stable.
- No face drift, morphing, warped anatomy, flicker, or changing expression.
- Keep the background perfectly uniform pure white (#FFFFFF) in every frame.
This is not a cinematic scene. It is a deterministic frame-scrubbing asset
for a website and every intermediate frame must work as a clean still image.
```
Inspect the middle frames as carefully as the endpoints. Face shape, ears, hairline, and clothing edges must remain stable throughout the MiniMax H3 output.
## 3. Repair prompt
Describe the failed generation precisely in `[[OBSERVED_PROBLEMS]]`.
```prompt
Regenerate this clip as a strict technical motion plate. The previous result
is unusable because: [[OBSERVED_PROBLEMS]].
Lock every property except the intended vertical gaze and head-tilt movement:
- preserve the exact identity and facial proportions in every frame;
- keep the horizontal head angle fixed at approximately 60 degrees left;
- fixed camera, crop, focal length, scale, head position, shoulders, torso,
arms, outfit, expression, lighting, and background;
- one slow linear movement from down-left to up-left;
- neutral horizontal-left pose at exactly two seconds;
- closed and motionless mouth;
- no speech, smile, blink during movement, eyebrow motion, body sway,
zoom, parallax, lighting shift, background flicker, face morphing,
hair change, ear deformation, or new objects;
- perfectly uniform pure white (#FFFFFF) background.
This clip will be paused and scrubbed frame by frame. Every intermediate frame
must remain anatomically coherent and visually consistent with the reference.
```
Example problem description:
```text
[[OBSERVED_PROBLEMS]] = the face changes near the final pose, the shoulders
move with the head, and the white background flickers between frames
```
## 4. Preparing the video for the web
AI video can play directly in a browser, but codec and keyframe interval matter when `currentTime` changes frequently. I prepared a 720×720, 30 FPS, silent H.264 file with every frame encoded as a keyframe.
```bash
ffmpeg -i INPUT.mp4 \
-vf "scale=720:720:force_original_aspect_ratio=decrease,pad=720:720:(ow-iw)/2:(oh-ih)/2:color=white,fps=30" \
-an -c:v libx264 -preset slow -crf 20 -pix_fmt yuv420p \
-g 1 -keyint_min 1 -sc_threshold 0 -movflags +faststart \
public/media/cursor-portrait/poyraz-bottom-right.mp4
```
Key decisions:
- `-an` removes audio completely.
- `yuv420p` improves Safari and Chromium compatibility.
- `faststart` moves MP4 metadata to the beginning.
- `-g 1` makes every frame independently seekable.
- `scale + pad` preserves proportions on a square white surface.
### Media optimization agent prompt
```prompt
Prepare [[INPUT_VIDEO_PATH]] as a web motion-control plate that will be scrubbed
forward and backward from pointer movement. Never overwrite the source file.
Outputs:
- Video: [[VIDEO_PATH]]
- Poster: [[POSTER_PATH]]
- Exact duration: 4.00 seconds
- Starting hold: 0.000.25
- Active motion: 0.253.75
- Final hold: 3.754.00
- Resolution: 720×720
- Frame rate: 30 FPS
- Codec: H.264 MP4, libx264, yuv420p
- Settings: preset slow, CRF 20, faststart, no audio
- Every frame, or at most every second frame, must be a keyframe
Do not distort the aspect ratio. Use #FFFFFF padding when needed. Do not crop
hair, face, ears, shoulders, or clothing. Verify duration, resolution, FPS,
codec, and file size. Visually inspect the first, middle, and final frames.
Do not modify unrelated project files.
```
## 5. Mapping pointer Y to video time
Pointer Y is `0` at the viewport top and `window.innerHeight` at the bottom. Invert and clamp it to `01`, then map it to the active video range.
```ts
const TOTAL_DURATION = 4;
const ACTIVE_START = 0.25;
const ACTIVE_END = 3.75;
const DEFAULT_TIME = 2;
const SMOOTHING = 0.12;
function clamp(value: number, minimum: number, maximum: number) {
return Math.min(Math.max(value, minimum), maximum);
}
function mapPointerYToTime(pointerY: number, viewportHeight: number) {
if (viewportHeight <= 0) return DEFAULT_TIME;
const progress = clamp(1 - pointerY / viewportHeight, 0, 1);
return ACTIVE_START + progress * (ACTIVE_END - ACTIVE_START);
}
```
| Pointer position | Progress | Video time | Gaze |
| --- | ---: | ---: | --- |
| Viewport bottom | `0` | `0.25` | down-left |
| Viewport middle | `0.5` | `2.00` | horizontal-left |
| Viewport top | `1` | `3.75` | up-left |
`pointerX` never enters this formula, so horizontal pointer movement does not change the frame.
## 6. Smooth scrubbing in React
Keep high-frequency values in refs instead of updating React state for every pointer event. A single `requestAnimationFrame` loop damps the current value toward the target.
```ts
const pointerYRef = useRef<number | null>(null);
const targetTimeRef = useRef(DEFAULT_TIME);
const currentTimeRef = useRef(DEFAULT_TIME);
const rafIdRef = useRef<number | null>(null);
function animate() {
const difference = targetTimeRef.current - currentTimeRef.current;
currentTimeRef.current += difference * SMOOTHING;
const video = videoRef.current;
if (video && Math.abs(video.currentTime - currentTimeRef.current) > 1 / 120) {
video.currentTime = currentTimeRef.current;
}
if (Math.abs(difference) > 0.002) {
rafIdRef.current = requestAnimationFrame(animate);
} else {
rafIdRef.current = null;
}
}
function handlePointerMove(event: PointerEvent) {
if (event.pointerType !== "mouse") return;
pointerYRef.current = event.clientY;
targetTimeRef.current = mapPointerYToTime(
event.clientY,
window.innerHeight,
);
if (rafIdRef.current === null) {
rafIdRef.current = requestAnimationFrame(animate);
}
}
```
Production code must also handle metadata readiness, optional video priming, tab visibility, and complete listener cleanup.
## 7. Ready-to-use coding-agent prompt
Replace the bracketed values and use this with a coding agent in an existing frontend project.
```prompt
Add a reusable `PointerPortraitFollower` component to the existing [[FRAMEWORK]]
project. It must stay at the bottom-right of the viewport and react only to the
pointer's Y position. Styling system: [[STYLING_SYSTEM]].
Assets:
- Video: [[VIDEO_PATH]]
- Poster: [[POSTER_PATH]]
System constants:
- TOTAL_DURATION = 4
- ACTIVE_START = 0.25
- ACTIVE_END = 3.75
- DEFAULT_TIME = 2
- SMOOTHING = 0.12
Behavior:
- Keep the video paused; never autoplay it normally.
- Use pointerY only. pointerX must never affect video timing.
- progress = clamp(1 - pointerY / window.innerHeight, 0, 1)
- targetTime = ACTIVE_START + progress * (ACTIVE_END - ACTIVE_START)
- Use a global pointermove listener without React state updates per event.
- Store pointerY, targetTime, and currentTime in refs.
- Apply damping in one requestAnimationFrame loop.
- Limit seeks to about 3060 Hz and skip tiny time differences.
- Return smoothly to the neutral 2.00-second pose when the pointer leaves the
window or the window loses focus.
Video element:
- muted, playsInline, preload="auto", no controls, no autoplay
- seek to 2.00 after loadedmetadata
- prime muted playback briefly on the first real pointer move only if required
- show the poster instead of a broken media icon after an asset error
Placement:
- position: fixed; right: [[RIGHT_OFFSET]]; bottom: [[BOTTOM_OFFSET]]
- width: [[DESKTOP_WIDTH]]; aspect-ratio: 1 / 1; z-index: [[Z_INDEX]]
- object-fit: contain; background: [[BACKGROUND_COLOR]]
- pointer-events: none; user-select: none; aria-hidden: true
- no border, radius, shadow, or horizontal mirroring
Responsive and lifecycle:
- disable animation on pointer: coarse and narrow viewports
- never interpret touch as mouse tracking
- honor prefers-reduced-motion
- do not block CTA, link, or menu interaction
- never access window/document during SSR
- stop RAF and seeking while the tab is hidden
- clean pointermove, pointerleave, blur, resize, visibilitychange, and RAF on
unmount; never start multiple RAF loops
Separate mapping and clamp into pure typed helpers. Add boundary tests when a
test setup exists. Do not add a heavy animation dependency. Run build,
typecheck, lint, and existing tests after implementation.
```
Values from this implementation:
```text
[[FRAMEWORK]] = Next.js App Router, React, TypeScript
[[STYLING_SYSTEM]] = Tailwind CSS and Poyraz UI
[[VIDEO_PATH]] = /media/cursor-portrait/poyraz-bottom-right.mp4
[[POSTER_PATH]] = /media/cursor-portrait/poyraz-bottom-right-poster.webp
[[RIGHT_OFFSET]] = 24px
[[BOTTOM_OFFSET]] = 0px
[[DESKTOP_WIDTH]] = clamp(110px, 11vw, 170px)
[[Z_INDEX]] = 40
[[BACKGROUND_COLOR]] = #FFFFFF
```
## 8. Mobile, accessibility, and fallback
This effect is meaningful on desktop with a mouse. Treating touch movement as pointer tracking hurts usability and wastes decoding work.
My choices:
- Do not render the component on `pointer: coarse` devices.
- Hide it completely below `840px`.
- Honor `prefers-reduced-motion`.
- Hide the white-background asset in dark mode.
- Keep it decorative with `pointer-events: none` and `aria-hidden="true"`.
- Show the poster if video loading fails.
## 9. Quality checklist
### Video
- Is it the same person in the first, middle, and final frames?
- Does the head stay turned about 60 degrees left?
- Are the poses down-left, horizontal-left, and up-left in the correct order?
- Do hair, ears, jaw, and facial features remain stable?
- Do shoulders and clothing stay still?
- Does the camera, light, or white background flicker?
- Does the motion remain natural when scrubbed backward?
### Web
- Does the video remain paused before pointer input?
- Does it scrub in the correct direction on vertical movement?
- Does horizontal-only movement leave the frame unchanged?
- Is there a seek queue or visible lag during fast movement?
- Does the portrait return to neutral after leaving the window?
- Are links and CTA controls still clickable?
- Is animation disabled on mobile and reduced-motion?
- Does the poster appear after a video error?
- Are listeners and RAF cleaned up after navigation?
## Adapt it to your project
Five steps are enough to reuse the system:
1. Replace every `[[...]]` variable for your character.
2. Produce a consistent 1:1 master frame on a flat background.
3. Describe only one intended motion axis in the MiniMax H3 prompt.
4. Map that same axis to the active video range.
5. Connect the optimized video and poster to the component.
The main rule is simple: do not invent motion in code that does not exist in the generated video. Treating the AI output as a controlled motion plate makes the effect more natural, deterministic, and testable.
## 10. Master-frame prompt for avatars
When the source is a 2D, 3D, or stylized avatar, prevent the model from reinterpreting its design language. This prompt locks the original medium and character identity.
```prompt
Use the uploaded avatar as the strict character-design reference. Recreate the
exact same character in a clean, production-ready master frame for a
mouse-following website animation.
Preserve exactly:
- the character's face design, head shape, hairstyle, colors, outfit,
accessories, proportions, material style, line style, shading language,
and overall visual identity;
- the original medium and aesthetic: [[AVATAR_STYLE]];
- all intentional asymmetries and recognizable features.
Do not turn a 2D avatar into 3D, do not turn a stylized avatar into a real
person, and do not redesign or improve the character.
Composition:
- [[ASPECT_RATIO]] frame.
- Medium close-up from [[CROP_POINT]] upward.
- Center the character, leaving enough space for the head to rotate up to
[[MAX_HEAD_ROTATION]] degrees toward [[TURN_DIRECTION]].
- Shoulders remain stable and mostly facing forward.
- Neutral starting pose, only [[STARTING_HEAD_ANGLE]] degrees toward
[[STARTING_DIRECTION]].
- Expression: [[EXPRESSION]].
Background:
- Completely flat, seamless [[BACKGROUND_COLOR]].
- No texture, gradient, cast shadow, props, text, logo, border, scenery, or UI.
Continuity requirements:
- Clean silhouette and stable edges.
- Consistent eyes and facial features according to the reference design.
- No additional accessories or design changes.
- Produce one character and one clean master frame only.
```
## 11. Motion prompts for every placement
The live example in this guide maps vertical pointer movement to a vertical gaze. The alternatives below produce frontal-to-profile clips driven by horizontal pointer movement. Reverse the turn for left-side placements so that the subject looks into the page instead of away from it.
### Bottom-right: portrait turning left
```prompt
Animate the uploaded master frame into a precise motion-control plate for an
interactive website portrait. Preserve the exact identity, face, hairstyle,
outfit, body proportions, lighting, colors, framing, and background.
Output:
- Duration: exactly 4.0 seconds.
- Aspect ratio: [[ASPECT_RATIO]].
- One continuous shot with a locked, eye-level camera.
- No zoom, crop change, pan, tilt, dolly, camera shake, speech, or audio motion.
The person will appear in the bottom-right corner. Website content and the
pointer will usually be to the person's left, so the motion must progress from
an almost frontal pose to a clear screen-left profile.
Timeline:
- 0.00-0.25: hold the reference pose, only [[STARTING_HEAD_ANGLE]] degrees left.
- 0.25-3.75: rotate smoothly and linearly toward screen-left.
- Reach approximately [[MAX_HEAD_ROTATION]] degrees in a clean left profile.
- 3.75-4.00: hold the final pose perfectly still.
The eyes lead slightly. Only eyes, head, and neck move. Shoulders, torso, arms,
clothing, scale, body position, and expression remain fixed. Keep the mouth
closed. No talking, smiling, eyebrow movement, nodding, leaning, breathing
motion, blinking during the turn, or secondary gestures.
Preserve identity and anatomy in every frame. No morphing, face drift, hair or
ear deformation, lighting change, clothing change, or background flicker.
Keep a perfectly flat [[BACKGROUND_COLOR]] background with no gradient, shadow,
texture, object, text, or logo.
This is a deterministic website animation plate intended to be paused and
scrubbed frame by frame, not a cinematic video.
```
Start with `[[MAX_HEAD_ROTATION]] = 85-90` and `[[STARTING_HEAD_ANGLE]] = 5-10` for a corner portrait.
### Bottom-left: portrait turning right
```prompt
Animate the uploaded master frame into a precise motion-control plate for an
interactive website portrait. Preserve the exact identity, face, hairstyle,
outfit, proportions, lighting, framing, and background.
The person will appear in the bottom-left corner, while most content and pointer
movement will be to the person's right.
Create exactly 4.0 seconds of one continuous, locked-off motion:
- Start almost facing the camera, only [[STARTING_HEAD_ANGLE]] degrees right.
- Hold the starting pose from 0.00 to 0.25 seconds.
- From 0.25 to 3.75 seconds, rotate smoothly and linearly toward screen-right.
- End at approximately [[MAX_HEAD_ROTATION]] degrees in a clean right profile.
- Hold that final pose from 3.75 to 4.00 seconds.
Only the eyes, head, and neck move. The eyes lead slightly and stay focused
toward screen-right. Shoulders, torso, arms, clothing, head scale, and body
position remain fixed. The camera is completely locked.
Keep the mouth closed and [[EXPRESSION]] unchanged. No speech, smile, lip or
eyebrow motion, nodding, leaning, blinking during the turn, breathing motion,
or gestures. No identity drift, morphing, hair change, warped profile, ear
deformation, lighting change, clothing change, or background flicker.
The background must remain perfectly flat [[BACKGROUND_COLOR]], without
shadows, gradients, props, text, logos, textures, or color variation. This is a
frame-scrubbable website plate, not a cinematic video.
```
### Hero-right: looking at the headline and CTA on the left
```prompt
Animate the uploaded identity-locked master frame for a website hero section.
The subject will be positioned on the right side; headline, copy, CTA buttons,
and pointer will be primarily on the left.
Create an exact 4.0-second locked-off motion-control clip. Start almost facing
the viewer at [[STARTING_HEAD_ANGLE]] degrees left and hold from 0.00 to 0.25.
From 0.25 to 3.75, smoothly rotate the eyes and head toward screen-left, ending
at [[MAX_HEAD_ROTATION]] degrees. Hold the final pose from 3.75 to 4.00.
The final pose must feel like the subject is looking at the hero headline and
CTA, not outside the page. Eyes lead slightly; the head follows in one slow,
continuous, linear movement.
Only eyes, head, and neck move. Keep shoulders, torso, arms, clothing, position,
scale, expression, and silhouette fixed. Mouth closed. No talking, smiling,
blinking during the turn, nodding, leaning, gestures, body sway, or breathing.
Preserve the exact person or avatar design. No face drift, morphing, hair or
outfit changes, lighting shifts, framing changes, or warped profile. Use a fixed
eye-level camera and a perfectly flat [[BACKGROUND_COLOR]] background.
Aspect ratio: [[ASPECT_RATIO]]. The result must be frame-scrubbable.
```
For hero layouts, `[[MAX_HEAD_ROTATION]] = 65-75` usually looks more natural.
### Hero-left: looking at the headline and CTA on the right
```prompt
Animate the uploaded identity-locked master frame for a website hero section.
The subject will be positioned on the left side; headline, copy, CTA buttons,
and pointer will be primarily on the right.
Create an exact 4.0-second locked-off motion-control clip. Start almost facing
the viewer at [[STARTING_HEAD_ANGLE]] degrees right and hold from 0.00 to 0.25.
From 0.25 to 3.75, smoothly rotate the eyes and head toward screen-right, ending
at [[MAX_HEAD_ROTATION]] degrees. Hold the final pose from 3.75 to 4.00.
The final pose must feel like the subject is looking at the hero headline and
CTA, not outside the page. Eyes lead slightly; the head follows in one slow,
continuous, linear movement.
Only eyes, head, and neck move. Keep shoulders, torso, arms, clothing, position,
scale, expression, and silhouette fixed. Mouth closed. No talking, smiling,
blinking during the turn, nodding, leaning, gestures, body sway, or breathing.
Preserve the exact person or avatar design. No identity drift, morphing, hair or
outfit changes, lighting shifts, framing changes, or warped profile. Use a fixed
eye-level camera and a perfectly flat [[BACKGROUND_COLOR]] background.
Aspect ratio: [[ASPECT_RATIO]]. The result must be frame-scrubbable.
```
### Hero-center: complete left-to-right scan
```prompt
Animate the uploaded identity-locked master frame into a symmetrical
left-to-right head-turn calibration clip for an interactive centered hero.
Output one continuous 4.0-second shot in [[ASPECT_RATIO]] with a locked,
eye-level camera. Preserve identity or avatar design, outfit, expression,
lighting, framing, scale, and [[BACKGROUND_COLOR]] background.
Timeline:
- 0.00-0.25: hold approximately [[LEFT_ANGLE]] degrees toward screen-left.
- 0.25-3.75: perform the complete symmetrical left-to-right rotation.
- Reach the exact front-facing pose at 50% of the active motion interval.
- Continue at the same speed to [[RIGHT_ANGLE]] degrees toward screen-right.
- 3.75-4.00: hold the final right-facing pose.
- Keep path, speed, scale, and head height symmetrical on both sides.
The eyes lead only slightly. Only eyes, head, and neck move. Shoulders, torso,
arms, clothing, body position, scale, and expression remain fixed. Mouth closed.
No speech, smile, blink during movement, eyebrow motion, nod, lean, gesture,
body sway, or breathing motion.
No identity drift, morphing, hairstyle change, ear deformation, warped profile,
lighting shift, background flicker, camera movement, zoom, crop, or reframing.
The background remains perfectly uniform [[BACKGROUND_COLOR]]. This must stay
clean when paused and scrubbed in either direction.
```
Use `[[LEFT_ANGLE]] = 75` and `[[RIGHT_ANGLE]] = 75` as a symmetric starting point.
## 12. Generic repair prompt
If the camera, shoulders, or mouth move, or if the profile loses identity, describe the defect precisely in `[[OBSERVED_PROBLEMS]]`.
```prompt
Regenerate this clip as a strict technical motion plate. The previous result is
unusable because it contains: [[OBSERVED_PROBLEMS]].
Lock every property except the intended head rotation:
- exact same identity and facial proportions in every frame;
- fixed camera, crop, focal length, scale, head position, shoulders, torso,
arms, outfit, expression, lighting, and background;
- only the eyes, head, and neck may move;
- one slow, linear rotation from [[STARTING_DIRECTION_AND_ANGLE]] to
[[ENDING_DIRECTION_AND_ANGLE]];
- closed and motionless mouth;
- no speech, smile, blink during the turn, eyebrow movement, nod, lean, body
sway, breathing, camera motion, zoom, parallax, lighting shift, background
flicker, face morphing, hair change, ear deformation, or new objects;
- perfectly uniform [[BACKGROUND_COLOR]] background;
- preserve the reference identity exactly, especially in the final profile.
This is a frame-scrubbing website asset, so every intermediate frame must be
anatomically coherent and usable as a still image.
```
## 13. Coding-agent prompt for horizontal variants
This prompt builds one reusable component for bottom-right, bottom-left, and hero placements. It uses `pointerX`, so treat it as an alternative to the vertical `pointerY` implementation earlier in this guide.
```prompt
Add a reusable mouse-following video portrait component to the existing
[[FRAMEWORK]] project. Styling system: [[STYLING_SYSTEM]]. Inspect the project's
structure, responsive rules, dependencies, and code conventions first.
Assets:
- Video: [[VIDEO_PATH]]
- Poster: [[POSTER_PATH]]
- Active motion: 0.25-3.75 seconds
- Video motion: [[VIDEO_MOTION_DESCRIPTION]]
- Placement: [[PLACEMENT]]
Behavior:
- Keep the video muted, playsInline, preload auto, paused, and without autoplay.
- Listen to global pointermove and measure the portrait anchor when needed.
- Map pointer position to 0-1 targetProgress, then to currentTime 0.25-3.75.
- Use RAF with lerp/damping. Do not update React state per pointer event.
- Limit seeks to 30-60 Hz and skip negligible time differences.
Direction mapping:
- For bottom-right or hero-right clips turning left: progress 0 near the
portrait and progress 1 as the pointer moves farther left.
- For bottom-left or hero-left clips turning right: progress 0 near the
portrait and progress 1 as the pointer moves farther right.
- For a hero-center clip scanning left-to-right, use pointerX / viewportWidth.
- Clamp progress to 0-1 and prevent anatomically invalid reverse turns.
Typed API:
- src, poster
- placement: bottom-right | bottom-left | hero-right | hero-left | hero-center
- defaultProgress, smoothing, desktopWidth, mobileWidth
- offsetX, offsetY, zIndex, className, decorative, invertProgress
Layout and lifecycle:
- Use fixed positioning for bottom-* and absolute positioning inside the hero
for hero-* variants.
- Use object-fit contain, a reserved aspect-ratio, and [[BACKGROUND_COLOR]].
- If decorative, use pointer-events none, user-select none, draggable false,
and aria-hidden true. Do not cover CTA controls or copy.
- Run client-side. Seek to [[DEFAULT_PROGRESS]] after loadedmetadata.
- If decoding needs it, prime muted playback on first real interaction and
immediately pause.
- Do not read layout every frame. Re-measure on resize/scroll at low cost.
- Stop RAF/seeking in hidden tabs and clean every listener and RAF on unmount.
- Disable tracking for coarse pointers and reduced motion; use
[[MOBILE_BEHAVIOR]]. Show the poster after video errors.
Use named constants TOTAL_DURATION=4, ACTIVE_START=0.25, ACTIVE_END=3.75.
Do not add a heavy animation library. Add complete TypeScript types and focused
mapping/clamp tests without refactoring unrelated files.
Report changed files, direction formula, build/typecheck/lint/test results, and
a four-item manual test checklist.
```
### Add only one new placement
```prompt
Do not break the behavior or public API of `CursorFollowerPortrait`. Add only a
new [[NEW_PLACEMENT]] variant.
Asset:
- Video: [[NEW_VIDEO_PATH]]
- Poster: [[NEW_POSTER_PATH]]
- Active motion: 0.25-3.75 seconds
- Motion: [[NEW_VIDEO_MOTION_DESCRIPTION]]
Placement and mapping:
- Placement: [[NEW_PLACEMENT]]
- Offset: [[HORIZONTAL_OFFSET]] horizontal, [[VERTICAL_OFFSET]] vertical
- Width: [[DESKTOP_WIDTH]] / mobile [[MOBILE_WIDTH]]
- Anatomical direction rule: [[DIRECTION_MAPPING_RULE]]
Do not change existing variants. Add a working usage example, run build,
typecheck, and lint, then report only changed files and verification results.
```
### Debugging prompt
```prompt
`CursorFollowerPortrait` has this problem: [[BUG_DESCRIPTION]].
Reproduce it first and identify the root cause with evidence. Check:
- assigning currentTime before metadata loads;
- slow seeking caused by codec or keyframe distance;
- incorrect progress direction or invertProgress;
- React renders on every pointer event;
- duplicate RAF loops or event listeners;
- getBoundingClientRect layout thrashing on every frame;
- Safari/iOS video priming behavior;
- asset path, CORS, preload, and poster fallback;
- incorrect reduced-motion or coarse-pointer detection;
- fixed/absolute containers and stacking contexts.
Do not refactor randomly before explaining the root cause. Apply the smallest
safe fix, preserve the public API, and report build/typecheck/lint/test results.
```
## 14. True two-axis tracking with a 3x3 grid
A single video can reliably follow only the axis it contains. For horizontal and vertical gaze, generate nine aligned poses from one master frame. For a real person, start with yaw values of `-35° / 0° / +35°` and pitch values of `-18° / 0° / +18°`.
| Pose | Yaw | Pitch |
| --- | ---: | ---: |
| Top-left | `[[YAW_LEFT]]` | `[[PITCH_UP]]` |
| Top-center | `0` | `[[PITCH_UP]]` |
| Top-right | `[[YAW_RIGHT]]` | `[[PITCH_UP]]` |
| Middle-left | `[[YAW_LEFT]]` | `0` |
| Center | `0` | `0` |
| Middle-right | `[[YAW_RIGHT]]` | `0` |
| Bottom-left | `[[YAW_LEFT]]` | `[[PITCH_DOWN]]` |
| Bottom-center | `0` | `[[PITCH_DOWN]]` |
| Bottom-right | `[[YAW_RIGHT]]` | `[[PITCH_DOWN]]` |
### Nine-direction image prompt
```prompt
Using the uploaded identity-locked master frame, create one exact directional
calibration pose for a 3x3 mouse-tracking portrait system.
Preserve the exact same identity, face, hairstyle, outfit, body, framing,
camera, focal length, lighting, scale, background, crop, and expression. This
image must align pixel-for-pixel as closely as possible with all other poses.
Change only:
- head yaw: [[TARGET_YAW]] degrees;
- head pitch: [[TARGET_PITCH]] degrees;
- eye gaze: [[GAZE_DIRECTION]], aligned naturally with the head direction.
Keep shoulders, torso, arms, clothing, body position, head center, and head
scale fixed. Mouth closed. No smile, speech, blink, eyebrow movement, body
turn, lean, camera motion, crop change, zoom, or lighting change.
Maintain anatomically correct neck, ears, eyes, jaw, and facial profile. No
morphing or identity drift. Use a perfectly flat [[BACKGROUND_COLOR]] identical
to the master frame. Output one image only at [[OUTPUT_DIMENSIONS]].
```
### Coding-agent prompt for the nine-direction system
```prompt
Add a `DirectionalPortraitGrid` component with true two-axis pointer tracking
to the existing [[FRAMEWORK]] project.
Nine equal-size, pixel-aligned assets:
- top-left: [[TOP_LEFT_PATH]]
- top-center: [[TOP_CENTER_PATH]]
- top-right: [[TOP_RIGHT_PATH]]
- middle-left: [[MIDDLE_LEFT_PATH]]
- center: [[CENTER_PATH]]
- middle-right: [[MIDDLE_RIGHT_PATH]]
- bottom-left: [[BOTTOM_LEFT_PATH]]
- bottom-center: [[BOTTOM_CENTER_PATH]]
- bottom-right: [[BOTTOM_RIGHT_PATH]]
Normalize the pointer relative to the portrait center and clamp both axes to
-1..1. Do not hard-switch to the nearest image. Find the surrounding four grid
cells, calculate bilinear interpolation weights, and blend those four aligned
images with opacity.
Use RAF and damping without React renders per pointer event. Preload assets
without blocking the page's LCP. Show the center image for reduced motion,
coarse pointers, or loading failures. If decorative, use pointer-events none
and aria-hidden true.
Typed props: sources, placement, desktopWidth, mobileWidth, smoothing,
maxTrackingDistance, offsetX, offsetY, className, decorative. Extract grid math
into pure functions and test corners, center, and intermediate values. Do not
add a heavy animation library. Add a usage example and report
build/typecheck/lint/test results.
```
## 15. Reels planning prompt
For a 55-second walkthrough, use this sequence: `0-3s` result, `3-7s` hook, `7-16s` master frame, `16-26s` motion video, `26-42s` coding, `42-51s` before/after and variant, `51-58s` CTA.
```prompt
I am a [[CREATOR_PROFILE]] software content creator. Plan an Instagram Reel for
this interactive website effect:
Effect: [[EFFECT_DESCRIPTION]]
Character: [[REAL_PERSON_OR_AVATAR]]
Placement: [[PLACEMENT]]
Generation tool: [[GENERATION_TOOL]]
Coding agent: [[CODING_AGENT]]
Audience: [[TARGET_AUDIENCE]]
Target duration: 55 seconds
Tone: [[TONE]]
Use this structure:
1. Show the result in the first two seconds as a visual hook.
2. A spoken hook of no more than ten words.
3. A concrete promise for what the viewer will build.
4. The reference-to-master-frame step.
5. Only the critical lines of the placement-specific motion prompt.
6. Only the critical technical logic from the coding prompt.
7. One beginner-friendly sentence explaining pointer-to-currentTime mapping.
8. A before/after scene.
9. An open loop into the next placement variant.
10. A short, natural CTA.
For every segment, provide its time range, spoken line, screen recording,
large on-screen text, and editing transition. Avoid exaggerated marketing,
unnecessary jargon, long intros, claims that AI did everything, or unrealistic
time promises. Highlight prompt fragments instead of displaying entire prompts.
Keep the total spoken script under 120 words.
```
This library lets you produce bottom-right, bottom-left, hero-right, hero-left, hero-center, and true 3x3 tracking assets from the same master design. Name each asset together with its motion direction, placement formula, and fallback to prevent direction mistakes as the system grows.