DEV Community

Cover image for Rive Data Binding and ViewModels for Interactive Characters
Mascot Engine
Mascot Engine

Posted on

Rive Data Binding and ViewModels for Interactive Characters

At Mascot Engine, we build interactive character systems that connect animation, product behaviour, and application data.

In this tutorial, we will use a sample Rive file named mascotengine.riv to demonstrate how Rive Data Binding and ViewModels can control an interactive character.

The sample Mascot Engine character will be able to:

  • Change emotions
  • Enter listening, thinking, and talking states
  • Follow pointer or touch positions
  • Display dynamic text
  • Change its colors at runtime
  • React to success and error events
  • Connect to React or Next.js application logic

Sample Mascot Engine file structure

Our sample mascotengine.riv file uses the following structure:

mascotengine.riv
├── Artboard: Mascot Engine
├── State Machine: Mascot Engine State Machine
└── ViewModel: MascotEngineViewModel
    ├── displayName
    ├── emotion
    ├── activity
    ├── isTalking
    ├── lookX
    ├── lookY
    ├── accentColor
    ├── celebrate
    └── showError
Enter fullscreen mode Exit fullscreen mode

This ViewModel becomes the public interface between the Mascot Engine character and the application.

The internal character rig, animation timelines, transitions, and blend states can change without requiring the developer to rewrite the integration, provided the ViewModel contract remains stable.

Add the Mascot Engine file

Place the sample file inside the application’s public directory:

public/
└── mascotengine.riv
Enter fullscreen mode Exit fullscreen mode

The file can then be loaded using:

src: '/mascotengine.riv'
Enter fullscreen mode Exit fullscreen mode

React implementation

'use client';

import {
  useRive,
  useViewModel,
  useViewModelInstance,
  useViewModelInstanceBoolean,
  useViewModelInstanceColor,
  useViewModelInstanceEnum,
  useViewModelInstanceNumber,
  useViewModelInstanceString,
  useViewModelInstanceTrigger,
} from '@rive-app/react-webgl2';

export default function MascotEngineCharacter() {
  const { rive, RiveComponent } = useRive({
    src: '/mascotengine.riv',
    artboard: 'Mascot Engine',
    stateMachines: 'Mascot Engine State Machine',
    autoplay: true,
    autoBind: false,
  });

  const mascotViewModel = useViewModel(rive, {
    name: 'MascotEngineViewModel',
  });

  const mascotInstance = useViewModelInstance(
    mascotViewModel,
    {
      name: 'MascotEngineDefault',
      rive,
    }
  );

  const {
    value: displayName,
    setValue: setDisplayName,
  } = useViewModelInstanceString(
    'displayName',
    mascotInstance
  );

  const {
    value: emotion,
    setValue: setEmotion,
  } = useViewModelInstanceEnum(
    'emotion',
    mascotInstance
  );

  const {
    setValue: setIsTalking,
  } = useViewModelInstanceBoolean(
    'isTalking',
    mascotInstance
  );

  const {
    setValue: setLookX,
  } = useViewModelInstanceNumber(
    'lookX',
    mascotInstance
  );

  const {
    setValue: setLookY,
  } = useViewModelInstanceNumber(
    'lookY',
    mascotInstance
  );

  const {
    setRgb: setAccentColor,
  } = useViewModelInstanceColor(
    'accentColor',
    mascotInstance
  );

  const {
    trigger: celebrate,
  } = useViewModelInstanceTrigger(
    'celebrate',
    mascotInstance
  );

  return (
    <section>
      <div
        style={{
          width: '100%',
          maxWidth: 600,
          height: 500,
          margin: '0 auto',
        }}
      >
        <RiveComponent />
      </div>

      <p>
        Character: {displayName ?? 'Mascot Engine'}
      </p>

      <p>
        Emotion: {emotion ?? 'Loading'}
      </p>

      <button
        type="button"
        onClick={() => setDisplayName?.('Nova')}
      >
        Change Name
      </button>

      <button
        type="button"
        onClick={() => setEmotion?.('thinking')}
      >
        Think
      </button>

      <button
        type="button"
        onClick={() => setIsTalking?.(true)}
      >
        Start Talking
      </button>

      <button
        type="button"
        onClick={() => {
          setLookX?.(0.7);
          setLookY?.(-0.2);
        }}
      >
        Look Right
      </button>

      <button
        type="button"
        onClick={() => setAccentColor?.(52, 120, 246)}
      >
        Change Theme
      </button>

      <button
        type="button"
        onClick={() => celebrate?.()}
      >
        Celebrate
      </button>
    </section>
  );
}
Enter fullscreen mode Exit fullscreen mode

How Mascot Engine structures interactive characters

At Mascot Engine, we treat the ViewModel as a stable contract between the character system and the application.

The application controls clear product-level properties such as:

activity = thinking
emotion = happy
isTalking = true
lookX = 0.5
lookY = -0.2
Enter fullscreen mode Exit fullscreen mode

The developer does not need to control individual bones, timelines, mouth shapes, eye layers, or internal animation transitions.

Those details remain inside mascotengine.riv.

This makes the character easier to:

  • Integrate
  • Test
  • Update
  • Reuse
  • Hand over to developers
  • Connect to AI or audio systems
  • Support across multiple platforms

Building an AI assistant with Mascot Engine

The sample Mascot Engine character can respond to an AI application lifecycle:

User starts recording
→ activity = listening

Application processes the request
→ activity = thinking

Audio response begins
→ activity = talking

Audio response finishes
→ activity = idle
Enter fullscreen mode Exit fullscreen mode

Example:

function setMascotActivity(
  activity: 'idle' | 'listening' | 'thinking' | 'talking'
) {
  setActivity?.(activity);

  if (activity === 'thinking') {
    setEmotion?.('thinking');
  }

  if (activity === 'talking') {
    setEmotion?.('happy');
  }

  if (activity === 'idle') {
    setEmotion?.('neutral');
  }
}
Enter fullscreen mode Exit fullscreen mode

This allows mascotengine.riv to function as a complete interactive character system instead of a collection of disconnected animation clips.

Need a production-ready interactive character?

Mascot Engine creates interactive character systems for:

  • AI products
  • SaaS platforms
  • Educational applications
  • Mobile apps
  • React and Next.js products
  • Flutter and React Native applications
  • Onboarding experiences
  • Virtual assistants and support agents

Our work can include:

  • Character preparation and rigging
  • Emotional animation systems
  • Interactive State Machines
  • Data Binding and ViewModels
  • Listening, thinking, and talking states
  • Pointer, touch, and gaze controls
  • Lip-sync and viseme systems
  • Runtime customization
  • Developer-ready documentation
  • Web and mobile integration preparation

We do not deliver only a looping character animation.

Mascot Engine builds a reusable character system that can respond to real application logic.

Explore Mascot Engine

Start an interactive mascot project


About the author

Praneeth Kawya Thathsara is a Rive Animator and Interactive Motion Designer specializing in production-ready character systems for AI, SaaS, web, and mobile products.

He is the founder of Mascot Engine.

Top comments (0)