npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@mediasemantics/react-embodied-agent

v1.0.2

Published

Add an embodied agent to your React app.

Readme

React Embodied Agent

Add an embodied agent to your React app! These realistic and cartoon characters can speak pre-authored and/or dynamic messages, with recorded or Text-to-Speech (TTS) audio, actions, emotions, and lip-sync.

example

Creating your module

Head over to https://mediasemantics.com and sign up for the People Builder service (free for a week, and as little as $10/month thereafter). Use the Modules tab, and use the Add button to create an Interactive Agent module. Configure your character's appearance, voice, background, and messages. Test it in the Preview tab. Return to the Modules tab, press the Publish button to publish your module, and then press Get Embed Code. You will see a React embed code similar to the following:

<ReactEmbodiedAgent ref={this.myAgentRef} style={{width:"250px", height:"200px"}} userid="12345678" moduleid="12345678" />

(The userid and moduleid will be different in your code.)

You can use Create React App and the following App.js file to turn your tag into a web page.

import { useRef } from 'react';
import ReactEmbodiedAgent from 'react-embodied-agent';
function App() {
    const myAgentRef = useRef(null);
    return ( 
        <ReactEmbodiedAgent ref={myAgentRef} style={{width:"250px", height:"200px"}} userid="12345678" moduleid="12345678" /> 
     );
}
export default App;

If you prefer the class syntax, you can use the equivalent class code:

import React, { Component } from 'react';
import ReactEmbodiedAgent from 'react-embodied-agent';
class App extends Component {
    constructor(props) {
        super(props);
        this.myAgentRef = React.createRef();
    }
    render() {
        return ( 
            <ReactEmbodiedAgent ref={this.myAgentRef} style={{width:"250px", height:"200px"}} userid="12345678" moduleid="12345678" />
        );
    }
}
export default App;

Playing messages

Use the play() method to trigger a message. Say you've used the People Builder's Messages tab to create a message named "Intro".

message

To play the "Intro" message, you use:

myAgentRef.current.play("Intro");

If you are using the class syntax, use:

this.myAgentRef.current.play("Intro");

You might run these commands in response to a button, e.g.

<button onClick={ () => myAgentRef.current.play("Intro") }>play</button>

You can use the stop() method to smoothly stop any playing messages. It's a good idea to do this before a play() if you want to interrupt any messages that may be playing:

myAgentRef.current.stop();
myAgentRef.current.play("Goodbye");

Without the stop(), the "Goodbye" message would simply be queued up, and would play as soon as the current message is finished.

Reacting to events

One important event is onModuleLoaded, which indicates that the module is loaded, that it's about to be displayed, and that it's ready to receive play() commands. Another event is onPlayComplete, which indicates that any playing messages have completed, and the agent has returned to the idle state.

Listen to events in the standard React way:

<ReactEmbodiedAgent ref={this.myAgentRef} style={{width:"250px", height:"200px"}} userid="12345678" moduleid="12345678" 
   onModuleLoaded={ () => console.log("module loaded") } />

Handling updates

You can have as many messages as you like. Playing messages is a bit like pulling strings on a puppet.

If you want to change a message, you can do so in the People Builder.

As soon as you Publish your changes to the module, they will instantly become live within your app. You do need to be connected to the internet, and have an active People Builder subscription, in order for the play() method to work. (To avoid this, you can also look into the Download HTML5 option in People Builder.)

Dynamic messages

If your message is not known at author-time, then you can also use the dynamicPlay() method. Say you want to include your user's name:

myAgentRef.current.dynamicPlay({do: "greet", say: "Welcome back, " + name + "."});

A message can consist of several sentences. Each sentence can have an associated "do" action, which you can think of as the manner in which the sentence is spoken. The "do" field values come straight from the dropdown at the beginning of each line in the Builder: just convert to lower-case and replace spaces with "-".

If you have several sentences, then you should speak them back-to-back. A typical pattern for dynamicPlay is:

myAgentRef.current.stop();
myAgentRef.current.dynamicPlay({do: "greet", say: "Welcome back, " + name + "."});
myAgentRef.current.dynamicPlay({say: "Can I tell you what happened while you were gone?"});

When you call dynamicPlay, the {do, say} record is added to the queue. The agent begins speaking as soon as the first record is added. When the sentence is complete, the record is removed from the queue and the next record begins.

Maybe you have a paragraph of text to speak. You can use the following code to break it down into a series of dynamicPlay statements:

function speakParagraph(paragraph) {
     myAgentRef.current.stop();
     let records = myAgentRef.current.scriptFromText(paragraph); // breaks into sentences
     for (let record of records)
          myAgentRef.current.dynamicPlay(record);
}

Breaking the paragaph down into sentences is necessary in order to lower the latency and to avoid a 255 character limit on the length of the "say" field.

Preloading messages

You can preload any message by using the preloadPlay() and preloadDynamicPlay() API. These work just like the regular play() and dynamicPlay() API, but simply ensures that any required resources are cached in the local browser cache.

Timing UI changes with your message

You can embed arbitrary commands in your message script that are surfaced as the onScriptCommand event in the event.detail field.

command

<ReactEmbodiedAgent ref={this.myAgentRef} style={{width:"250px", height:"200px"}} userid="12345678" moduleid="12345678" 
   onScriptCommand={ (e) => processCommand(e.detail) } />

You often use commands with "do" actions such as "Look" and "Point". When you do, the event occurs at a "natural" point in the action. The command is just a string, and you can apply your own rules on how to interpret it.

function processCommand(s) {
    let command = s.split(" ")[0];
    let arg = s.split(" ")[1];
    if (command == "show") show(arg);
}

Additional API

This Readme covers the basics, but please visit our documentation for a full description of the API.