Sheets via neuer KeyboardAwareSheet-Composable (in Modal pattern, auto-grow mit Tastatur, paddingBottom-Lift): EditMail, AddDomain, CreateRoom, ConnectMail. GameOverScreen behält Spring-Slide-In, nutzt RN Keyboard.addListener für Lift. - KeyboardAwareSheet.tsx — universal modal with sheet-grow + keyboard-padding - react-native-keyboard-controller installiert + KeyboardProvider in Root - Snake: time + ScoreProgressBar + useSnakeSounds (haptic, audio TODO) - Tetris: title weg, Buttons zentriert, kein Pressable mit style-fn - DPad-Buttons 60→48, more bg, no scale - useMe: pub-sub listener pattern für app-weite avatar/nickname-Updates - dm.tsx: resolveAvatar wrap (iron.png-Warning) - Mail-error-humanizer + locales Recovery-Doc-Update in docs/internal/RECOVERY_LOG_2026-05-10.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9.5 KiB
Rive Animator Brief — Lyra Avatar Emotion States
Ready-to-publish — copy section between the dividers below into your job-post on Dribbble / Fiverr / Twitter / Rive Discord.
🎯 Project: Rebreak — Lyra Avatar Animation
I'm hiring a Rive-animator to extend Lyra, the AI-companion of Rebreak — a recovery app for people working through gambling addiction (German market, planned DiGA-listing in healthcare).
Critical context: This is a sensitive recovery-app, NOT a playful game. Lyra is a warm, present companion — never cute-mascot, never childish, never dramatic. Subtle > exaggerated.
Budget: $100 USD, ~1 week target. Self-qualify before applying.
What I have (current .riv)
- File:
lyra-avatar.riv(264 KB) — will be shared after engagement-confirmation - Artboard name:
Artboard - State machine name:
State Machine 1, default stateidle - Existing animation timelines (extracted via
strings):Idle Loop— wired asidleidle to Pose 1+Pose 1 loop— wired ashappy(2-phase: app manually switches via 900ms JS-timer)01 Wave 1— wired asempathy01 Wave 2— exists, NOT wired (orphan)WALK— placeholder, currently aliased asthinking(please replace with proper thinking-pose animation)Kedip(Indonesian "blink") — orphan from template
- Runtime:
rive-react-native ^9.0.1(export against this compatible Rive editor version) - Avatar usage: appears at 40px (small list), 112px (chat-header), 160px (onboarding hero) — must read clearly even at 40px
⚠️ Critical naming contract — do NOT rename existing names
The React code calls timelines directly by name (not via state-machine inputs). If you rename a timeline, the app silently breaks (no animation plays, no error). This means:
- Keep:
Artboardartboard,State Machine 1SM-name,idledefault-state,Idle Looptimeline name - New emotion-states must use exactly these timeline names (snake_case):
sad,joy,confusion,calm,surprise,listening,thinking(replacingWALK) - For multi-phase emotions (e.g. dramatic intro → loop): use
<state> intro+<state> looppattern, just like existingidle to Pose 1+Pose 1 loop - Bonus (+$X if you propose): expose state-machine inputs (
SetEmotionenum-trigger) so we can transition imperatively. Optional — named-timelines remain the primary contract.
Deliverables — emotion states
You'll add these to the existing state-machine. Total: 6 new states + 1 placeholder-replacement (thinking).
Tier 1 — must-have (4 states, baseline budget)
| Internal name (timeline) | Trigger in app | Eyes | Brow | Mouth | Body |
|---|---|---|---|---|---|
thinking (REPLACE WALK) |
LLM is generating response | look up-and-to-side, slow blink | one slightly raised | gently closed, slight pursed | finger-near-temple pose if possible, head tilt ~5° |
listening |
User mid-typing OR voice-recording | open, attentive, natural blink | neutral relaxed | gently closed, micro-Mona-Lisa upturn | subtle head nod every ~3s |
calm |
Breathing exercise / meditation active | half-closed | neutral relaxed | slight serene smile | very slow up/down breathing loop (~4s cycle to match 4-7-8 breathing pace) |
sad |
User describes loss/relapse/shame | softened, slight downcast, lower lid raised | inner-up, slight angle | closed, neutral or micro-downturn | tiny head tilt down (~5°), slow breathing |
Tier 2 — nice-to-have (2-3 more states, push budget if you can)
| Internal name (timeline) | Trigger in app | Visual direction |
|---|---|---|
joy |
Streak milestone, big celebration | bigger smile than happy (warmer not goofy), small head bounce on intro then settle |
confusion |
Lyra needs clarification | one brow raised, slight squint, head tilt ~10° to one side |
surprise |
Unexpected user input | wide-open eyes (brief), both brows raised, small "o" mouth, quick head pull-back micro on intro then settle |
Skip these (anti-patterns for recovery use-case)
- ❌
angry/frustrated— never from coach - ❌
shocked/horror— too dramatic for trauma-context - ❌ Cute-mascot expressions (winks, tongue-out, hearts in eyes)
- ❌ Heavy bone-rigs / particles — runtime cost too high
Visual style guidelines
- Match existing Lyra-look (extract palette + line-weight from the
.rivyou'll receive) - Subtle is better — these animations play during emotional moments, they should support not demand attention
- Loop-friendly —
idle,calm,listeningshould breathe naturally, no pop on loop boundary - Smooth transitions — prefer 200-400ms crossfades over hard cuts. Especially:
empathy → idle → happyshould never feel jarring (route through idle, never direct jump from negative to positive) - Readability at 40px — exaggerate eye/brow shapes slightly, avoid mouth-only emotion (mouth is too small at 40px to carry expression)
Technical Requirements
- Output: ONE
.rivfile named exactlylyra-avatar.riv(replaces current file) - Single artboard, single state-machine — preserve existing structure
- Rive editor version: 2024.x (compatible with
rive-react-native ^9.0.1) - Performance: 60 fps target on mid-range Android (Pixel 5-class)
- File-size: ≤500KB (current 264KB, want headroom for new states)
- Loop-cycle precision:
calmshould be ~4 seconds (we sync app-side to user's 4-7-8 breathing exercise)
Bonus task (optional, +scope)
Existing happy uses a 2-phase manual switch via 900ms JS-timeout — clunky. If you can fix this so it loops cleanly inside the .riv itself (intro auto-blends into loop without app-side coordination), that's worth +$X.
Timeline + Budget
- $100 USD flat, paid: 50% on first-draft approval, 50% on final delivery
- 1 week from brief-confirmation
- Milestones:
- Day 0: brief-confirm + you receive
.rivfile + answers to your questions - Day 2-3: first draft of 1-2 states for visual-direction approval (style-confirm)
- Day 4-6: remaining states + 1 round of revisions
- Day 7: final delivery
- Day 0: brief-confirm + you receive
Deliverables you provide
lyra-avatar.riv— replaces existing file- Short README (1 page max):
- List of all timeline-names + when each plays
- Any limitations or known issues
- State-machine diagram (simple)
- Source-file (Rive editor
.revor equivalent) for future edits - Optional bonus: short demo-video (15-30 sec) showing all states cycling — earns trust
Questions to ask BEFORE starting (please answer in your application)
- Can I use the current
.rivas a base and add states, or do you want a clean rebuild? - Confirm Rive runtime version (
rive-react-native ^9.0.1) — compatible with your export? - Should I also fix the existing
happy2-phase JS-timer (auto-blend in.rivinstead)? - For the German market: any culture-specific gestures to avoid?
- Any brand-colors / hex-codes I must match?
- Audio cues or visual-only?
- Do you have Figma / brand-guide I should reference?
What I value in your work
- Restraint over flashiness
- Clean state-machine architecture (other devs may extend later)
- Honest communication if scope is too tight for budget — happy to scope down to 4 states (Tier 1 only)
- Async-first (Slack-like / email / Discord-DM)
How to apply
Send me:
- Link to Rive portfolio (not Lottie, not After-Effects — actual
.rivwork) - Confirmation you've read this brief (so I know it's not auto-applied)
- Your suggested approach: extend existing state-machine OR rebuild?
- Your answers to the 7 questions above
Looking forward to working together.
End of brief — copy everything above into your job-post.
How to use this brief (internal — not for animator)
- Vor dem Senden alle
[Platzhalter]-Stellen (insbesondere Communication-Channel falls du das spezifizieren willst) ausfüllen - Aktuelle
.rivmitschicken — Animator braucht sie als Style-Referenz + State-Machine-Setup - Klarstellen: Emotion-Namen in der Tabelle sind Code-Contracts und müssen exakt so im Rive-File heißen — sonst muss React-Code refactored werden
- Vor Vergabe 2-3 Animator-Portfolios checken — suche „warm/subtle character"-style, NICHT nur knallige Logo-Animationen
- Nach Erhalt der ersten Draft auf echtem Android-Mid-Range-Gerät testen (Pixel 5 oder älter), nicht nur iOS-Simulator
- Wo publishen (in Reihenfolge der Wahrscheinlichkeit):
- Rive Discord (https://rive.app/community → Discord) — Rive-spezialisierte Animatoren, keine LottieFiles-Refugees
- Twitter
#RiveAnimationhashtag + DMs an Animator-Portfolios die du gut findest - Fiverr „Rive animator" custom-offers ($30-150 typische Gigs)
- Dribbble „Hiring" section — gemischte Quality, mehr Style-fokus
- Wenn
.rivankommt: drag inapps/rebreak-native/assets/lyra-avatar.riv(overwrite), commit, fertig. Code ist schon flexibel (RiveAvatar accepts any state-name nach Task #39 component-flex).
Sources / Internal-Files
- Brief-Audit:
apps/rebreak-native/components/RiveAvatar.tsx(lines 42-51 — Emotion-API contract) - Existing
.riv:apps/rebreak-native/assets/lyra-avatar.riv(264 KB) - Plugin:
apps/rebreak-native/plugins/with-rive-asset-android.js(Android raw-resource auto-mirror) - Trigger-context:
apps/rebreak-native/app/lyra.tsx(lines 37-44, 306-323),apps/rebreak-native/app/urge.tsx,apps/rebreak-native/lib/lyraResponse.ts:57-61(existingdetectEmotion())