A cinematic, story-driven 75-second video. One family. One real day. OiMy in the background — quiet, present, human.
"Oh — someone finally built something that understands what family life actually feels like."
Not "wow, this AI is powerful." Not "this is a smart product." The feeling of being understood. Of exhaling. Of not being alone in the chaos.
OiMy is not the hero. The family is. OiMy is the quiet friend who texts at exactly the right moment.
Shot on a warm prime lens — 35mm or 50mm equivalent. Shallow depth of field. Slightly underexposed, lifted in post for warmth. Film grain overlay at 8% opacity. No clean, perfected advertising look — this should feel like a memory, not a commercial.
Warm shadows (#2A1808 base). Amber highlights pulled toward #E8A86A. Skin tones protected — rich, warm. Desaturate the blues slightly. Think Kodak Vision3 500T, not clean digital. Reference: The Fabelmans kitchen scenes.
Caveat (handwritten, Google Fonts) for emotional captions. Lora (serif) for the OiMy logo reveal. All text appears with a gentle ink-draw animation — not a sharp slide. Handwritten captions sit in the lower-third, not centered, like a note left on a fridge.
Phone screen appears as a floating element over the live footage — not a full-screen insert. Soft drop shadow, slight tilt (natural hand hold angle, ~4°). The OiMy message bubbles use the real app design: cream background, amber accent, Lora font at 14px.
Reference films for the director/editor
Warm family chaos: Tully (2018) opening 10 min. Kitchen intimacy: Chef's Table first 2 episodes. Phone-as-character: Her ambient UI scenes. Product emotion: Apple's "Welcome Home" Spike Jonze spot — the feeling of a space that knows you.
Follow Maya (mom, 34), Rahul (dad, 36), two kids Aanya (7) and Dev (2), and Nani (grandma, 68, lives nearby). OiMy appears as a quiet presence — a warm glow on a phone screen. It never speaks out loud. It never announces itself. It simply knows what the family needs, a moment before they know they need it.
Pure AI video (Sora alone, or Runway alone) will feel clean and slightly dead. The goal is warmth and humanity. The recommended approach splits the work by what each tool does best.
Best current tool for realistic human faces with emotional nuance. Use Image-to-Video mode: generate a reference frame first (Midjourney/Firefly), then animate it. This gives character consistency across shots. Turbo mode is faster but Gen-3 Alpha gives richer motion. Budget: ~20 credits per shot.
Currently the most photorealistic human video generator. Better skin texture and natural movement than Runway. Weaker on complex scenes (multiple people). Use for the solo moments: Maya's face, Rahul's relief, the quiet couch scene. Kling Pro mode for the emotional close-ups.
All phone screen UI, message bubble animations, handwritten caption reveals, the logo close, and the platform badge sequence are built in Remotion (React/TypeScript). This gives pixel-perfect control over the OiMy brand moments — no keyframe approximation. Script below.
Apply the warm color grade to all AI-generated footage. Use the Kodak Vision3 500T emulation LUT from Ground Control (free), then manually push shadows toward #2A1808 and highlights toward amber. Add 8% film grain via the Fusion compositor. This is what makes AI footage feel real.
Generate character reference frames for Runway Image-to-Video. Create one consistent "family" look: Maya (South Asian, 34, warm expression), Rahul (36, office attire, slightly tired eyes), Aanya (7, school uniform variant), Dev (2, round face, pajamas). Use Character Reference to maintain consistency.
Use the "Matilda" voice or "Rachel" voice — warm, slightly intimate, never announcer-style. Speak at 85% of default speed. Tiny room reverb (0.15s decay). The three VO lines should feel like someone sitting next to you, not a commercial. Generate multiple takes, use the most human one.
On consistency across shots
The biggest challenge with AI video is making the same character look the same shot-to-shot. The solution: generate a "character bible" of 12–15 reference images per person in Midjourney using Character Reference mode. Feed these consistently into Runway as the start frame. Accept slight variation — it actually reads as warmth and humanity, not error.
This Remotion composition handles: phone UI overlays with OiMy chat bubbles, handwritten caption reveals, the logo close sequence, and the platform badge animation. Install with npm create video@latest then drop these components in.
// OiMy Explainer — Remotion composition // Handles: phone overlays, caption reveals, logo close // npm create video@latest && npm i @remotion/player import { AbsoluteFill, Sequence, Video, Img, useCurrentFrame, interpolate, spring, useVideoConfig, Audio } from 'remotion'; import { CSSProperties } from 'react'; // ── Brand tokens ──────────────────────────────────────── const BRAND = { amber: '#C8722A', amberLt: '#E8A86A', sage: '#5D8A5E', cream: '#FDF9F4', ink: '#2A2018', inkMid: '#5C4E3A', }; // ── Utility: spring-eased opacity ─────────────────────── function useFadeIn(startFrame: number, duration = 18) { const frame = useCurrentFrame(); return interpolate(frame, [startFrame, startFrame + duration], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', }); } // ── OiMy message bubble ───────────────────────────────── function MessageBubble({ text, from, startFrame, width = 300, }: { text: string; from: 'oimy' | 'user'; startFrame: number; width?: number; }) { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); const opacity = useFadeIn(startFrame); const translateY = interpolate( frame, [startFrame, startFrame + 20], [14, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } ); const isOiMy = from === 'oimy'; const style: CSSProperties = { background: isOiMy ? 'rgba(253,249,244,0.96)' : 'rgba(200,114,42,0.85)', color: isOiMy ? BRAND.ink : '#fff', borderRadius: isOiMy ? '18px 18px 18px 4px' : '18px 18px 4px 18px', padding: '14px 18px', maxWidth: width, fontFamily: 'Lora, Georgia, serif', fontSize: 14, lineHeight: 1.6, boxShadow: '0 4px 24px rgba(0,0,0,0.18)', alignSelf: isOiMy ? 'flex-start' : 'flex-end', opacity, transform: `translateY(${translateY}px)`, }; return ( <div style={style}> {isOiMy && ( <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: BRAND.amber, marginBottom: 5 }}> OiMy </div> )} {text} </div> ); } // ── Phone frame overlay ───────────────────────────────── function PhoneOverlay({ startFrame, children }: { startFrame: number; children: React.ReactNode }) { const frame = useCurrentFrame(); const opacity = useFadeIn(startFrame, 12); const scale = interpolate( frame, [startFrame, startFrame + 14], [0.95, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } ); return ( <div style={{ position: 'absolute', bottom: '12%', right: '8%', width: 280, background: 'rgba(26,18,8,0.92)', borderRadius: 24, padding: '20px 16px', border: '1px solid rgba(200,114,42,0.3)', boxShadow: '0 24px 60px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,255,255,0.05)', display: 'flex', flexDirection:'column', gap: 10, transform: `scale(${scale}) rotate(-2deg)`, opacity, backdropFilter: 'blur(8px)', }}> {/* Status bar */} <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11, color: 'rgba(253,245,236,0.4)', marginBottom: 4 }}> <span>9:23</span><span>●●●●●</span> </div> {children} </div> ); } // ── Handwritten caption ───────────────────────────────── // Uses SVG stroke-dashoffset trick for ink-draw effect function HandwrittenCaption({ text, startFrame }: { text: string; startFrame: number }) { const frame = useCurrentFrame(); const opacity = useFadeIn(startFrame, 24); const clipProgress = interpolate( frame, [startFrame, startFrame + 36], [0, 100], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' } ); return ( <div style={{ position: 'absolute', bottom: '8%', left: '7%', fontFamily: 'Caveat, cursive', fontSize: 34, color: '#FDF5EC', textShadow: '0 2px 24px rgba(0,0,0,0.75)', opacity, // Clip reveal left-to-right for ink draw feel clipPath: `inset(0 ${100 - clipProgress}% 0 0)`, letterSpacing: '0.02em', }}> {text} </div> ); } // ── Logo close sequence ───────────────────────────────── function LogoClose() { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); const logoOpacity = interpolate(frame, [12, 30], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }); const logoScale = spring({ fps, frame, from: 0.96, to: 1.0, delay: 12, durationInFrames: 24 }); const taglineOpacity = interpolate(frame, [36, 54], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }); const taglineClip = interpolate(frame, [36, 72], [0, 100], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }); const badgesOpacity = interpolate(frame, [72, 90], [0, 1], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }); const badgesY = interpolate(frame, [72, 90], [12, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }); // Breathing scale (heartbeat feel) const breathe = 1 + 0.008 * Math.sin((frame / fps) * 2 * Math.PI * 0.4); return ( <AbsoluteFill style={{ background: '#1A1208', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 24 }}> {/* OiMy wordmark */} <div style={{ fontFamily: 'Lora, Georgia, serif', fontSize: 64, fontWeight: 500, color: BRAND.cream, letterSpacing: '-0.02em', opacity: logoOpacity, transform: `scale(${logoScale * breathe})`, }}> OiMy </div> {/* Tagline — ink draw reveal */} <div style={{ fontFamily: 'Caveat, cursive', fontSize: 24, color: BRAND.amberLt, opacity: taglineOpacity, clipPath: `inset(0 ${100 - taglineClip}% 0 0)`, letterSpacing: '0.02em', maxWidth: 520, textAlign: 'center', lineHeight: 1.4, }}> For families who already have enough to worry about. </div> {/* Platform badges */} <div style={{ display: 'flex', gap: 20, opacity: badgesOpacity, transform: `translateY(${badgesY}px)`, marginTop: 16, }}> {['Telegram', 'WhatsApp', 'iOS'].map(p => ( <div key={p} style={{ padding: '8px 20px', border: '1px solid rgba(253,249,244,0.2)', borderRadius: 100, color: 'rgba(253,249,244,0.6)', fontSize: 13, fontFamily: 'Inter, sans-serif', fontWeight: 500, }}>{p}</div> ))} </div> </AbsoluteFill> ); } // ── Amber glow pulse (OiMy "alive" indicator) ─────────── function AmberPulse({ startFrame }: { startFrame: number }) { const frame = useCurrentFrame(); const { fps } = useVideoConfig(); const progress = Math.max(0, frame - startFrame); const scale = 1 + 0.3 * Math.sin((progress / fps) * 2 * Math.PI * 1.2); const opacity = interpolate(progress, [0, 8, 16], [0, 0.4, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }); return ( <div style={{ position: 'absolute', right: '9%', bottom: '13%', width: 300, height: 200, borderRadius: 50, background: BRAND.amber, filter: 'blur(60px)', opacity, transform: `scale(${scale})`, pointerEvents:'none', }} /> ); } // ── Root composition ──────────────────────────────────── export function OiMyExplainer() { const { fps } = useVideoConfig(); const f = (sec: number) => Math.round(sec * fps); // seconds → frames at 30fps return ( <AbsoluteFill style={{ background: '#1A1208' }}> {/* ── Scene 1: Alarm (0:00–0:04) */} <Sequence from={f(0)} durationInFrames={f(4)}> <Video src="./footage/shot-01-alarm.mp4" /> </Sequence> {/* ── Scene 2: Kitchen chaos (0:04–0:14) */} <Sequence from={f(4)} durationInFrames={f(10)}> <Video src="./footage/shot-02-kitchen.mp4" /> </Sequence> {/* ── Scene 3: OiMy first message (0:14–0:22) */} <Sequence from={f(14)} durationInFrames={f(8)}> <Video src="./footage/shot-03-maya-phone.mp4" /> <AmberPulse startFrame={0} /> <PhoneOverlay startFrame={6}> <MessageBubble from="oimy" text="School pickup at 2:30 today. Want me to remind you at 2:10? 🌿" startFrame={8} /> </PhoneOverlay> </Sequence> {/* ── Scene 4: Dad at work (0:22–0:34) */} <Sequence from={f(22)} durationInFrames={f(12)}> <Video src="./footage/shot-04-dad-office.mp4" /> <PhoneOverlay startFrame={4}> <MessageBubble from="user" text="Did Dad take his pills today?" startFrame={6} /> <MessageBubble from="oimy" text="Yes ✓ — marked at 9:15am. He also asked me to tell you: "I'm feeling good today."" startFrame={18} /> </PhoneOverlay> </Sequence> {/* ── Scene 5: The meltdown (0:34–0:46) */} <Sequence from={f(34)} durationInFrames={f(12)}> <Video src="./footage/shot-05-meltdown.mp4" /> <PhoneOverlay startFrame={4}> <MessageBubble from="user" text="she won't stop crying and I don't know what to do" startFrame={6} width={240} /> <MessageBubble from="oimy" text="That sounds really exhausting. You're doing great, even when it doesn't feel like it. Is this a hunger cry, or overtired? — Last Tuesday she calmed down with music. Want me to start it?" startFrame={22} /> </PhoneOverlay> </Sequence> {/* ── Scene 6: Evening dinner (0:46–0:58) */} <Sequence from={f(46)} durationInFrames={f(12)}> <Video src="./footage/shot-06-dinner.mp4" /> <HandwrittenCaption text="Dinner happened." startFrame={f(10) - f(46)} /> </Sequence> {/* ── Scene 7: Night / couch (0:58–1:06) */} <Sequence from={f(58)} durationInFrames={f(8)}> <Video src="./footage/shot-07-night.mp4" /> <PhoneOverlay startFrame={4}> <MessageBubble from="oimy" text="Tomorrow: Aanya's school project is due. Dentist at 9. I'll remind you in the morning. Rest up. 🌙" startFrame={6} /> </PhoneOverlay> </Sequence> {/* ── Scene 8: Logo close (1:06–1:15) */} <Sequence from={f(66)} durationInFrames={f(9)}> <LogoClose /> </Sequence> {/* ── Audio: voiceover */} <Audio src="./audio/voiceover.mp3" /> <Audio src="./audio/score.mp3" volume={0.55} /> </AbsoluteFill> ); } // ── Register composition in Root.tsx ──────────────────── // <Composition // id="OiMyExplainer" // component={OiMyExplainer} // durationInFrames={75 * 30} // 75 seconds at 30fps // fps={30} // width={1080} // 9:16 primary // height={1920} // />
Rendering for both formats
Render 1080×1920 (9:16) as the primary — this is what gets shared on WhatsApp and Telegram. Then re-render at 1920×1080 (16:9) for the website hero. In the 16:9 version, the phone overlay moves to the right third of frame. Adjust the right and bottom positioning in PhoneOverlay accordingly. Use npx remotion render OiMyExplainer --codec=h264 for both.
The score does as much emotional work as the visuals. It must feel like it was composed specifically for this family — not licensed background music, not corporate uplift. Intimate. Slightly imperfect. Human.
Slow enough to breathe. D major for warmth without saccharine. The score "warms up" as the day progresses — first chord at 0:14, full arrangement by 0:46.
Layer 1 (0:14): Solo Rhodes piano — warm, slightly worn. Layer 2 (0:28): Acoustic guitar fingerpicking enters, no plectrum. Layer 3 (0:46): Single cello joins — not dramatic, just present. Dissolves at 1:06 back to solo piano for the logo.
At 0:34 (the meltdown scene), the music drops to pure room tone for 3 seconds. Then a single cello note enters — not a melody, just a held note, like a breath. This is the most important sound design decision in the entire video.
"Arrival in Nara" by Ólafur Arnalds (warmth without sweetness). "On the Nature of Daylight" by Max Richter (emotional weight). "Comptine d'un autre été" — but slower, less precious. Commission from Artlist.io or Musicbed using "intimate piano family warmth" as search terms.
Underneath the score for the first 14 seconds: coffee percolator, a child's unintelligible chatter in another room, the scrape of a chair. These sounds ground the viewer before the music begins. They are not mixed low — they are present. Real kitchens are loud in the morning.
At the dinner scene: the sound of a wooden spoon against a pot, a child's laugh (unscripted-sounding), cutlery. These sounds should barely peek above the music mix — they live in the background, not the foreground. They make the visuals true.
The one sound that must be perfect
The OiMy notification sound — heard once, at 0:14 when the first message arrives. It must NOT be a standard phone ping. It should be a single, warm piano note — D4, soft attack, 0.8 second decay. This is the "voice" of OiMy. Commission this as a custom sound asset. Budget $50 from a sound designer on Fiverr or SoundSnap. It will be used across all OiMy product touchpoints.
Use these as Image-to-Video prompts in Runway Gen-3, or text-to-video in Sora. Generate 3–5 variants per shot and select the one with the most natural human movement. Do not use the first generation.
1080×1920 (9:16). H.264, CRF 18. Under 50MB for WhatsApp. Under 2GB for Telegram. 30fps. 75-second cut. AAC audio, stereo, 48kHz. Color space: BT.709.
1920×1080 (16:9). Same specs. The Remotion composition needs a layout variant — the phone overlay shifts to the right third. 90-second extended cut adds 15 seconds to the dinner scene.
Render a version with subtitles burned in (white Caveat font, amber shadow) and music only — no voiceover dialogue. Autoplay loops on websites without audio. The visual story must still work completely silently.
Export the Remotion composition as a transparent ProRes 4444 with alpha — this lets the video editor drop the phone overlays, captions, and logo close over the color-graded footage in Resolve without re-rendering Remotion for each grade pass.