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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lemonsliceai/avatar

v0.1.0

Published

Browser helpers for integrating LemonSlice avatars

Readme

@lemonsliceai/avatar

Browser helpers for integrating LemonSlice avatars.

The package centers on avatar readiness — knowing the exact moment an avatar has rendered its first frame and is ready to display, so you can hide loaders, fade the avatar in, or kick off the rest of your UX at the right time.

It ships three entry points so you can pick the one that matches your stack:

| Import | Use when | | --- | --- | | @lemonsliceai/avatar/livekit-react | You use LiveKit + React (recommended) | | @lemonsliceai/avatar/react | You use React with a MediaStreamTrack from any source | | @lemonsliceai/avatar | Plain JavaScript / TypeScript, no framework |

Installation

npm install @lemonsliceai/avatar

react, @livekit/components-react, and livekit-client are optional peer dependencies — install only the ones your chosen entry point needs. The plain JS entry (@lemonsliceai/avatar) has no peer dependencies.


LiveKit + React (recommended)

<LiveKitAvatarReadyWatcher> is a renderless component that you drop inside your <LiveKitRoom> tree. It automatically finds the LemonSlice avatar's video track and fires onReady when the avatar is streaming A/V:

import { useState } from "react";
import { LiveKitRoom } from "@livekit/components-react";
import { LiveKitAvatarReadyWatcher } from "@lemonsliceai/avatar/livekit-react";

function AvatarStage({ serverUrl, token }: { serverUrl: string; token: string }) {
  const [ready, setReady] = useState(false);

  return (
    <LiveKitRoom serverUrl={serverUrl} token={token} connect>
      <LiveKitAvatarReadyWatcher onReady={() => setReady(true)} />

      {!ready && <LoadingSpinner />}
      <div style={{ opacity: ready ? 1 : 0, transition: "opacity 200ms" }}>
        {/* your <VideoTrack /> / avatar UI */}
      </div>
    </LiveKitRoom>
  );
}

Props

| Prop | Type | Default | Description | | --- | --- | --- | --- | | onReady | () => void | — | Called exactly once when the avatar is ready. | | frameThreshold | number | 1 | Number of decoded frames to wait for. | | botReadyFallbackDelayMs | number | 5000 | Delay after a bot_ready RPC before firing as a fallback. | | avatarParticipantIdentity | string | "lemonslice-avatar-agent" | LiveKit identity that publishes the avatar track. |


React (any track source)

If you already have the avatar's MediaStreamTrack (from Daily, WebRTC, or anywhere else), useAvatarReady is a hook that fires onReady once the track has rendered its first frame(s). It tears down automatically on unmount, when the track changes, or when enabled is false.

import { useState } from "react";
import { useAvatarReady } from "@lemonsliceai/avatar/react";

function Avatar({ track }: { track: MediaStreamTrack | null }) {
  const [ready, setReady] = useState(false);

  useAvatarReady(track, {
    onReady: () => setReady(true),
  });

  return ready ? <AvatarVideo track={track} /> : <LoadingSpinner />;
}

Pass null (or enabled: false) while you don't yet have a track — the hook simply stays idle until a track is available.

Options

| Option | Type | Default | Description | | --- | --- | --- | --- | | onReady | () => void | — | Called once when the avatar is ready. | | frameThreshold | number | 1 | Number of decoded frames to wait for. | | enabled | boolean | true | Set false to pause watching without unmounting. |


Plain JavaScript / TypeScript

waitForAvatarReady is the framework-agnostic core. Give it a video MediaStreamTrack and it calls onReady once the first frame(s) decode. It returns a dispose function for cleanup.

import { waitForAvatarReady } from "@lemonsliceai/avatar";

const stop = waitForAvatarReady(videoTrack, {
  onReady: () => {
    console.log("avatar is ready — first frame rendered");
    showAvatar();
  },
});

// Cancel early if you navigate away before it fires:
stop();

You can also observe individual frames as they arrive:

const stop = waitForAvatarReady(videoTrack, {
  frameThreshold: 3, // wait for 3 decoded frames
  onFrame: (frameIndex) => console.log(`frame ${frameIndex} decoded`),
  onReady: () => showAvatar(),
});

It is safe to call in non-browser environments (e.g. SSR): when document is unavailable it returns a no-op dispose and does nothing.

Options

| Option | Type | Default | Description | | --- | --- | --- | --- | | onReady | () => void | — | Called once when frameThreshold frames have decoded. | | frameThreshold | number | 1 | Number of decoded frames to wait for. | | onFrame | (frameIndex: number) => void | — | Optional per-frame hook (frameIndex starts at 1). |


Browser support

Readiness detection relies on the browser's HTMLVideoElement.requestVideoFrameCallback() API to observe decoded video frames. This is supported across all modern browsers (Baseline since 2024) but may be unavailable in older browsers. Where it is unsupported, the frame-based signal will not fire. In that case, fall back to the LemonSlice bot_ready WebRTC message, as done by LiveKitAvatarReadyWatcher.