This repository demonstrates an initialization race condition with Rive's nested inputs when the default state differs from the programmatically set state.
When initializing multiple nested inputs (revealed and type) on streak elements in a Rive animation:
-
Under normal conditions:
- Streaks with
revealed: false, type: 0appear as empty outlined circles - Streaks with
revealed: true, type: 1appear as filled grey circles - The
revealanimation correctly animates the filled circles with fire
- Streaks with
-
Under slower network conditions (Fast 4G or slower):
- Streaks that should be
revealed: false, type: 0incorrectly appear asrevealed: true, type: 0 - This occurs because the default state (
revealed: true) is racing with our programmatic state setting (revealed: false)
- Streaks that should be
The issue was caused by a race condition between:
- The state machine's default initialization of the
revealedstate totrue - Our programmatic attempt to set
revealedtofalse
Under slower network conditions, the race condition becomes more apparent as the timing between state machine initialization and our state updates becomes less predictable.
Setting the default state of revealed to false in the Rive editor resolves the issue. This ensures that both:
- The initial state matches our intended state
- There's no race condition between default initialization and programmatic updates
This suggests a best practice for Rive animations with programmatically controlled states:
- Default states in the Rive file should match the initial states you plan to set programmatically
- When states appear to be racing, check the default values in the Rive editor
- Consider documenting default states as part of the animation's API
-
Clone this repository
-
Install dependencies:
pnpm install
-
Start the development server:
pnpm dev
-
Open Chrome DevTools
-
Set network throttling to "Fast 4G" or slower
-
Observe that streaks initialized with
revealed: falseappear asrevealed: true
The reproduction uses:
- React 18.3.1
- @rive-app/react-canvas ^4.17.5
- TypeScript
- Vite
The key code setting the states:
typescript
const initialize = (rive: Rive) => {
// Should appear as empty outlined circle
rive.setBooleanStateAtPath("revealed", false, "streak-0");
rive.setNumberStateAtPath("type", 0, "streak-0");
// Should appear as filled grey circle
rive.setBooleanStateAtPath("revealed", true, "streak-2");
rive.setNumberStateAtPath("type", 1, "streak-2");
};
- Streaks initialized with
revealed: false, type: 0should consistently appear as empty outlined circles - State initialization should be reliable regardless of network conditions
- Under slower network conditions, streaks initialized with
revealed: falseappear as ifrevealed: true - Despite having a "ready" event handler, the state initialization happens in a separate effect when the
riveobject becomes available - This suggests the
riveobject might be available before the runtime is fully initialized - The issue might be related to how the state machine handles nested input initialization:
- Default states might be set after our initialization
- There could be implicit dependencies between
revealedandtypestates - The "ready" event might fire before nested inputs are fully initialized
- Move the state initialization into the "ready" event handler instead of a separate effect
- Add a delay before state initialization
- Implement retry logic for state initialization
- Consider moving initial states to the Rive file itself
- Investigate in Rive Editor:
- Check default values of nested inputs in State Machine Inspector
- Examine state machine initialization order
- Look for implicit state dependencies
- Consider adding state machine breakpoints to debug initialization sequence
- Browser: Chrome (latest)
- Network: Issue reproduces on Fast 4G or slower
- React: 18.3.1
- Rive React Canvas: ^4.17.5

