> ## Documentation Index
> Fetch the complete documentation index at: https://na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# About Embody Avatars and Agents

> Real-time interactive 3D avatars for education, training, and communication, powered by Livepeer.

export const embodySocials = [{
  icon: 'globe',
  href: 'https://embody.zone/',
  label: 'Embody'
}, {
  icon: 'github',
  href: 'https://github.com/its-DeFine/Unreal_Vtuber',
  label: 'Embody GitHub'
}, {
  icon: 'youtube',
  href: 'https://www.youtube.com/@Embody-Media',
  label: 'Embody Media YouTube'
}, {
  icon: 'comment-dots',
  href: 'https://forum.livepeer.org/t/embody-unreal-engine-updates/3186',
  label: 'Livepeer Forum'
}, {
  icon: 'discord',
  href: 'https://discord.gg/gVmDMhYm',
  label: 'Embody Discord'
}];

export const embodyInfra = [{
  icon: 'plug',
  label: 'api'
}, {
  icon: 'cloud',
  label: 'saas'
}];

export const embodyBadges = [{
  color: 'purple',
  label: 'AI'
}, {
  color: 'green',
  label: 'Realtime AI'
}, {
  color: 'red',
  label: 'Agents'
}];

export const StyledStep = ({title, icon, titleSize = 'h3', iconColor = null, titleColor = null, children, className = '', style = {}, ...rest}) => {
  const styledTitle = titleColor ? <span style={{
    color: titleColor
  }}>{title}</span> : title;
  return <Step title={styledTitle} icon={icon} iconColor={iconColor || undefined} titleSize={titleSize} className={className} style={style} {...rest}>
      {children}
    </Step>;
};

export const StyledSteps = ({children, iconColor, titleColor, lineColor, iconSize = '24px', className = '', style = {}, ...rest}) => {
  const resolvedIconColor = iconColor || 'var(--accent-dark, #18794E)';
  const resolvedTitleColor = titleColor || 'var(--lp-color-accent)';
  const resolvedLineColor = lineColor || 'var(--lp-color-accent)';
  return <div className={['docs-styled-steps', className].filter(Boolean).join(' ')} style={style} {...rest}>
      <style>{`
        .docs-styled-steps .steps > div > div.absolute > div {
          background-color: ${resolvedIconColor};
        }
        .docs-styled-steps .steps > div > div.w-full > p {
          color: ${resolvedTitleColor};
        }
        .docs-styled-steps .steps > div > div.absolute.w-px {
          background-color: ${resolvedLineColor};
        }
        .docs-styled-steps .steps > div:last-child > div.absolute.w-px::after {
          content: '';
          position: absolute;
          bottom: 0;
          left: 50%;
          transform: translateX(-50%);
          width: 6px;
          height: 6px;
          background-color: ${resolvedLineColor};
          transform: translateX(-50%) rotate(45deg);
        }
      `}</style>
      <div>
        <Steps>{children}</Steps>
      </div>
    </div>;
};

export const IconBadgeWrapper = ({items = [], iconColor, size = 14, gap = "var(--lp-spacing-3)", style = {}, className = '', ...rest}) => {
  const wrapperStyle = {
    display: 'flex',
    flexWrap: 'wrap',
    alignItems: 'center',
    gap,
    margin: '0.25rem 0 0.5rem'
  };
  const tagStyle = {
    display: 'inline-flex',
    alignItems: 'center',
    gap: "var(--lp-spacing-1)",
    fontSize: `${size}px`,
    color: 'var(--lp-color-text-primary)'
  };
  return <div className={className} style={{
    ...wrapperStyle,
    ...style
  }} {...rest}>
      {items.map((item, i) => <span key={i} style={tagStyle}>
          <Icon icon={item.icon} size={size} color={iconColor || 'currentColor'} />
          {item.label}
        </span>)}
    </div>;
};

export const BadgeWrapper = ({badges, children, gap = '0.4rem', margin = '0.5rem 0 1.5rem 0', style = {}, className = '', ...rest}) => {
  const defaultStyle = {
    display: 'flex',
    flexWrap: 'wrap',
    alignItems: 'center',
    gap,
    margin
  };
  return <div className={className} style={{
    ...defaultStyle,
    ...style
  }} {...rest}>
      {badges ? badges.map((b, i) => <Badge key={i} color={b.color}>
              {b.label}
            </Badge>) : children}
    </div>;
};

export const Video = ({src, title = "", author = "", caption, href = "", controls = true, autoPlay = false, loop = false, muted = false, children, className = "", style = {}, ...rest}) => {
  const buildCaption = () => {
    if (!author && caption) return caption;
    if (!author && !title) return null;
    return <span style={{
      display: "flex",
      flexDirection: "column",
      alignItems: "center",
      textAlign: "center",
      lineHeight: 1.2
    }}>
        <span>
          {author && (href ? <span style={{
      display: "flex",
      width: "100%",
      height: "fit-content",
      gap: "var(--lp-spacing-2)"
    }}>
                <a href={href} target="_blank" rel="noopener noreferrer">
                  <Icon icon="microphone" size={16} />
                  <span style={{
      borderBottom: "2px solid var(--lp-color-accent)",
      marginLeft: "0.2rem"
    }}>
                    <strong>{author}</strong>
                  </span>
                  <span style={{
      alignSelf: "flex-end",
      marginLeft: "var(--lp-spacing-px-6)"
    }}>
                    <Icon icon="arrow-up-right" size={12} color="var(--lp-color-accent)" />
                  </span>
                </a>
              </span> : <>
                <Icon icon="microphone" size={16} /> <strong>{author}</strong>
              </>)}
          {author && title ? ` • ${title}` : title}
        </span>
        {caption && <span style={{
      marginTop: "var(--lp-spacing-2)",
      fontStyle: "italic"
    }}>
            {caption}
          </span>}
      </span>;
  };
  const captionContent = buildCaption();
  const [linkHovered, setLinkHovered] = useState(false);
  return <div style={{
    position: "relative",
    display: "block"
  }}>
      {href && <a href={href} target="_blank" rel="noopener noreferrer" aria-label={title || author || "External link"} onMouseEnter={() => setLinkHovered(true)} onMouseLeave={() => setLinkHovered(false)} style={{
    position: "absolute",
    top: "var(--lp-spacing-2)",
    right: "var(--lp-spacing-3)",
    zIndex: "2",
    display: "inline-flex",
    alignItems: "center",
    justifyContent: "center",
    padding: "var(--lp-spacing-px-6)",
    background: linkHovered ? "var(--lp-color-border-default)" : "transparent",
    border: `1.5px solid ${linkHovered ? "var(--lp-color-accent)" : "var(--lp-color-text-primary)"}`,
    borderRadius: "6px",
    textDecoration: "none",
    lineHeight: 0,
    cursor: "pointer",
    transition: "all 0.15s ease"
  }}>
          <Icon icon="arrow-up-right" size={14} color={linkHovered ? "var(--lp-color-accent)" : "var(--lp-color-text-primary)"} />
        </a>}
      <Frame className={className} style={style} {...captionContent ? {
    caption: captionContent
  } : {}} {...rest}>
        <video controls={controls} autoPlay={autoPlay} loop={loop} muted={muted} playsInline className="w-full aspect-video rounded-xl" src={src} title={title || author || "Video"} />
        {children}
      </Frame>
    </div>;
};

export const YouTubeVideo = ({embedUrl, url, title = "", author = "", hint = "", caption, className = "", style = {}, ...rest}) => {
  const toEmbedUrl = value => {
    if (!value || typeof value !== "string") return "";
    const source = value.trim();
    if (!source) return "";
    try {
      const parsed = new URL(source);
      const host = parsed.hostname.replace(/^www\./, "");
      if (host === "youtube.com" || host === "youtube-nocookie.com") {
        if (parsed.pathname.startsWith("/embed/")) return source;
        const videoId = parsed.pathname.startsWith("/shorts/") ? parsed.pathname.split("/").filter(Boolean)[1] : parsed.searchParams.get("v");
        if (!videoId) return "";
        const params = new URLSearchParams(parsed.search);
        params.delete("v");
        const query = params.toString();
        return `https://www.youtube.com/embed/${videoId}${query ? `?${query}` : ""}`;
      }
      if (host === "youtu.be") {
        const videoId = parsed.pathname.split("/").filter(Boolean)[0];
        if (!videoId) return "";
        const query = parsed.searchParams.toString();
        return `https://www.youtube.com/embed/${videoId}${query ? `?${query}` : ""}`;
      }
    } catch (_err) {
      return "";
    }
    return "";
  };
  const resolvedEmbedUrl = toEmbedUrl(embedUrl || url);
  if (!resolvedEmbedUrl) {
    return null;
  }
  const isValidYouTubeUrl = resolvedEmbedUrl.includes("youtube.com/embed/");
  if (!isValidYouTubeUrl) {
    console.warn("Invalid YouTube embed URL:", embedUrl || url);
    return null;
  }
  const buildCaption = () => {
    if (caption) return caption;
    if (!author && !title) return null;
    return <span style={{
      display: "flex",
      flexDirection: "column",
      alignItems: "center",
      textAlign: "center",
      lineHeight: 1.2
    }}>
        <span>
          {author && <>
              <Icon icon="microphone" size={16} /> <strong>{author}</strong>
            </>}
          {author && title ? `${" "} • ${title}` : title}
        </span>
      </span>;
  };
  const captionContent = buildCaption();
  return <Frame className={className} style={style} {...hint ? {
    hint
  } : {}} {...captionContent ? {
    caption: captionContent
  } : {}} {...rest}>
      <iframe className="w-full aspect-video rounded-xl" src={resolvedEmbedUrl} title={title || author || "YouTube Video"} allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />
    </Frame>;
};

export const CenteredContainer = ({children, maxWidth = "800px", padding = "0", preset = "default", width = "", minWidth = "", marginRight = "", marginBottom = "", textAlign = "", style = {}, className = "", ...rest}) => {
  const presets = {
    default: {},
    fitContent: {
      width: "fit-content",
      minWidth: "fit-content"
    },
    readable70: {
      width: "70%",
      minWidth: "fit-content"
    },
    readable80: {
      width: "80%",
      minWidth: "fit-content"
    },
    readable90: {
      width: "90%"
    },
    wide900: {
      maxWidth: "900px"
    }
  };
  const presetStyle = presets[preset] || presets.default;
  return <div className={className} style={{
    maxWidth: presetStyle.maxWidth || maxWidth,
    margin: "0 auto",
    padding: padding,
    ...presetStyle.width ? {
      width: presetStyle.width
    } : {},
    ...presetStyle.minWidth ? {
      minWidth: presetStyle.minWidth
    } : {},
    ...width ? {
      width
    } : {},
    ...minWidth ? {
      minWidth
    } : {},
    ...marginRight ? {
      marginRight
    } : {},
    ...marginBottom ? {
      marginBottom
    } : {},
    ...textAlign ? {
      textAlign
    } : {},
    ...style
  }} {...rest}>
      {children}
    </div>;
};

export const BorderedBox = ({children, variant = "default", padding = "var(--lp-spacing-4)", borderRadius = "var(--lp-spacing-px-8)", accentBar = "", style = {}, className = "", ...rest}) => {
  const variants = {
    default: {
      border: "1px solid var(--lp-color-border-default)",
      backgroundColor: "var(--lp-color-bg-card)"
    },
    accent: {
      border: "1px solid var(--lp-color-accent)",
      backgroundColor: "var(--lp-color-bg-card)"
    },
    muted: {
      border: "1px solid var(--lp-color-border-default)",
      backgroundColor: "transparent"
    }
  };
  const accentBarColors = {
    accent: "var(--lp-color-accent)",
    positive: "var(--green-9)"
  };
  return <div data-docs-bordered-box="" data-accent-bar={accentBarColors[accentBar] ? "" : undefined} className={className} style={{
    ...variants[variant],
    padding: padding,
    borderRadius: borderRadius,
    ...accentBarColors[accentBar] ? {
      position: "relative",
      '--accent-bar-color': accentBarColors[accentBar]
    } : {},
    ...style
  }} {...rest}>
      {children}
    </div>;
};

export const SocialLinks = ({links, size = 20, gap = "var(--lp-spacing-3)", justify = "center", iconColor, color, className = "", style = {}, ...rest}) => {
  const resolvedIconColor = iconColor || color;
  const linkStyle = {
    border: "none",
    borderBottom: "none",
    textDecoration: "none",
    display: "inline-flex"
  };
  const colors = {
    discord: resolvedIconColor || "var(--lp-color-brand-discord)",
    twitter: resolvedIconColor || "var(--lp-color-text-primary)",
    github: resolvedIconColor || "var(--lp-color-brand-github)",
    forum: resolvedIconColor || "var(--lp-color-brand-forum)",
    website: resolvedIconColor || "var(--lp-color-accent)",
    blog: resolvedIconColor || "var(--lp-color-accent)",
    globe: resolvedIconColor || "var(--lp-color-brand-globe)",
    twitch: resolvedIconColor || "var(--lp-color-brand-twitch)",
    youtube: resolvedIconColor || "var(--lp-color-brand-youtube)",
    instagram: resolvedIconColor || "var(--lp-color-brand-instagram)",
    linkedin: resolvedIconColor || "var(--lp-color-brand-linkedin)"
  };
  const iconColorMap = {
    discord: "discord",
    "x-twitter": "twitter",
    github: "github",
    "comment-pen": "forum",
    "pen-line": "blog",
    "pencil-line": "blog",
    globe: "globe",
    "book-open": "website",
    twitch: "twitch",
    youtube: "youtube",
    instagram: "instagram",
    linkedin: "linkedin"
  };
  const defaultLinks = [{
    icon: "discord",
    href: "https://discord.com/invite/livepeer",
    label: "Livepeer Discord"
  }, {
    icon: "globe",
    href: "https://livepeer.org",
    label: "Livepeer Website"
  }, {
    icon: "github",
    href: "https://github.com/livepeer",
    label: "Livepeer GitHub"
  }, {
    icon: "comment-pen",
    href: "https://forum.livepeer.org",
    label: "Livepeer Forum"
  }, {
    icon: "pen-line",
    href: "https://livepeer.org/blog",
    label: "Livepeer Blog"
  }, {
    icon: "x-twitter",
    href: "https://x.com/livepeer",
    label: "Livepeer X"
  }];
  const items = links || defaultLinks;
  return <div className={className} style={style} {...rest}>
      <style>{`
        .social-links a {
          border: none;
          border-bottom: none;
        }
      `}</style>
      <span className="social-links" style={{
    display: "flex",
    justifyContent: justify,
    gap: gap,
    marginTop: "var(--lp-spacing-2)"
  }}>
        {items.map((item, i) => <a key={i} href={item.href} target="_blank" rel="noopener noreferrer" aria-label={item.label} style={linkStyle}>
            <Tooltip headline={item.label}>
              <Icon icon={item.icon} size={size} color={colors[iconColorMap[item.icon] || "website"] || "var(--lp-color-accent)"} aria-hidden="true" />
            </Tooltip>
          </a>)}
      </span>
    </div>;
};

export const CustomCardTitle = ({icon, title, variant = "card", iconSize, style = {}, className = "", ...rest}) => {
  const variants = {
    card: {
      display: 'flex',
      alignItems: 'center',
      gap: "var(--lp-spacing-2)",
      marginBottom: "var(--lp-spacing-3)",
      color: 'var(--lp-color-text-primary)',
      fontSize: '1rem',
      fontWeight: 600
    },
    accordion: {
      display: 'inline-flex',
      alignItems: 'center',
      gap: "var(--lp-spacing-2)"
    },
    tab: {
      display: 'inline-flex',
      alignItems: 'center',
      gap: '0.4rem',
      fontSize: '0.875rem'
    }
  };
  const sizes = {
    card: 20,
    accordion: 18,
    tab: 14
  };
  const size = iconSize || sizes[variant] || 20;
  const baseStyle = variants[variant] || variants.card;
  return variant === 'card' ? <div className={className} style={{
    ...baseStyle,
    ...style
  }} {...rest}>
      {typeof icon === 'string' ? <Icon icon={icon} size={size} color="var(--lp-color-accent)" /> : icon}
      {title}
    </div> : <span className={className} style={{
    ...baseStyle,
    ...style
  }} {...rest}>
      {typeof icon === 'string' ? <Icon icon={icon} size={size} color="var(--lp-color-accent)" /> : icon}
      {title}
    </span>;
};

export const CustomDivider = ({color = "var(--lp-color-border-default)", middleText = "", spacing = "default", style = {}, className = "", ...rest}) => {
  const spacingPresets = {
    default: {
      margin: "24px 0"
    },
    overlap: {
      margin: "-1rem 0 -1rem 0"
    },
    tight: {
      margin: "0 0 -1rem 0"
    },
    section: {
      margin: "0 0 -2rem 0"
    },
    sectionOverlap: {
      margin: "-1rem 0 -2rem 0"
    },
    deepOverlap: {
      margin: "-1rem 0 -1.5rem 0"
    }
  };
  const spacingStyle = spacingPresets[spacing] || spacingPresets.default;
  return <div role="separator" aria-orientation="horizontal" className={className} style={{
    display: "flex",
    alignItems: "center",
    ...spacingStyle,
    fontSize: style?.fontSize || "16px",
    height: "fit-content",
    ...style
  }} {...rest}>
      <span style={{
    marginRight: "var(--lp-spacing-px-8)",
    opacity: 0.2
  }}>
        <Icon icon="/snippets/assets/logos/Livepeer-Logo-Symbol-Theme.svg" />
      </span>
      <div style={{
    flex: 1,
    height: "1px",
    background: "var(--lp-color-border-default)",
    opacity: 0.4
  }}></div>
      {middleText && <>
          <Icon icon="circle" size={2} />
          <span style={{
    margin: "0 8px",
    fontWeight: "bold",
    color: color,
    opacity: 0.7
  }}>
            {middleText}
          </span>
          <Icon icon="circle" size={2} />
        </>}
      <div style={{
    flex: 1,
    height: "1px",
    background: "var(--lp-color-border-default)",
    opacity: 0.4
  }}></div>
      <span style={{
    marginLeft: "var(--lp-spacing-px-8)",
    opacity: 0.2
  }}>
        <span style={{
    display: "inline-block",
    transform: "scaleX(-1)"
  }}>
          <Icon icon="/snippets/assets/logos/Livepeer-Logo-Symbol-Theme.svg" />
        </span>
      </span>
    </div>;
};

<IconBadgeWrapper style={{ margin: "-1rem 0 1rem 0" }} iconColor="var(--accent)" size={16} items={embodyInfra} />

<BadgeWrapper style={{margin: "0 0 1rem 0"}} badges={embodyBadges} />

<CenteredContainer maxWidth="fit-content">
  <BorderedBox style={ {margin: 0, padding: "0.5rem 1rem 1rem 1rem"}} variant="accent">
    <SocialLinks iconColor="var(--text)" links={embodySocials} />
  </BorderedBox>
</CenteredContainer>

<Info>
  This page is maintained by the Embody Team. Help & support available via the [Livepeer Discord]('https://discord.gg/livepeer').
</Info>

<Video src="https://raw.githubusercontent.com/livepeer/docs/docs-v2-assets/snippets/assets/domain/10_PRODUCTS/Embody/Videos/arealiensreal.mp4" alt="Embody Avatars" author="Embody Media" caption="Video Prompt: What is the closest evidence we have that aliens exist?" href="https://embody.zone/" />

Embody builds **real-time interactive 3D avatars** for education, training, and communication. Avatars are AI-driven and respond to prompts and questions live, enabling interactive video experiences at scale.

Embody runs on the Livepeer Network, using decentralised GPU infrastructure for real-time AI inference and video delivery.

<CustomDivider style={{margin: "-0.5rem 0 -2.5rem 0"}} />

## Key Features

* **Interactive 3D avatars** - AI-driven avatars that respond to text prompts and questions in real time.
* **Live video streaming** - Powered by Livepeer for low-latency, decentralised video delivery.
* **Education and training** - Purpose-built for interactive learning, onboarding, and communication use cases.
* **Unreal Engine integration** - Avatar rendering via Unreal Engine for high-fidelity, real-time 3D output.

<YouTubeVideo embedUrl="https://www.youtube.com/embed/_MAM5ZPsTdM" title="Embody — real-time interactive 3D avatars" />

<CustomDivider style={{margin: "0 0 -2.5rem 0"}} />

## Try Embody

<CardGroup cols={2}>
  <Card title={<CustomCardTitle icon="wand-magic-sparkles" title="Try Embody Interface" />} href="https://embody.zone/" img="https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Human-App.png?fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=64b64e91ae3302428c018a8dfae521f3" data-og-width="800" width="800" data-og-height="450" height="450" data-path="snippets/assets/media/heros/solutions/embody/Embody_Hero_Human-App.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Human-App.png?w=280&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=f14f331bf677e6c1bccf853208d39146 280w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Human-App.png?w=560&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=c51cad94b7cb3ab48f5171d62b1b5f0c 560w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Human-App.png?w=840&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=f9cb310c40af68e5a056e349f0545a0c 840w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Human-App.png?w=1100&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=9bf175c785384eaa6571e14ad6946bad 1100w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Human-App.png?w=1650&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=d097eca4058ae372d4e997dcf66703b0 1650w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Human-App.png?w=2500&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=2e2ca4c602d850c033dfa834c4bab0f1 2500w">
    Try the human Avatar Interface - Interact with a live AI avatar in real time
  </Card>

  <Card title={<CustomCardTitle icon="user-robot" title="Agents on Embody" />} href="https://embody.zone/" img="https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Agent-App.png?fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=bfd77eb2be5acb5cc05d97984282d5cc" data-og-width="800" width="800" data-og-height="450" height="450" data-path="snippets/assets/media/heros/solutions/embody/Embody_Hero_Agent-App.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Agent-App.png?w=280&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=87ddb6b3c0155cde680aa8f847e3421c 280w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Agent-App.png?w=560&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=1d21acfa38b565e641c56b5dbf24c326 560w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Agent-App.png?w=840&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=a06eb9425c1a333f93af27c6f63fbd3e 840w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Agent-App.png?w=1100&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=f2a8493b23626c71dd86857c9072c8f1 1100w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Agent-App.png?w=1650&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=24ee7de807f0d12044205aa6c2686811 1650w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Agent-App.png?w=2500&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=62c1d329d4d046591090f7697a2c0115 2500w">
    Try Embody Openclaw Agent Workflows
  </Card>

  <Card title={<CustomCardTitle icon="comment-dots" title="Livepeer Forum" />} href="https://forum.livepeer.org/t/embody-unreal-engine-updates/3186" img="https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Updates.png?fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=cd2664a036d8dcfa86fe8eb341c5a01f" data-og-width="800" width="800" data-og-height="450" height="450" data-path="snippets/assets/media/heros/solutions/embody/Embody_Hero_Updates.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Updates.png?w=280&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=a4575b51461641118a5a358da75b8966 280w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Updates.png?w=560&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=8923c8f039625953ff30072fa8ce36f0 560w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Updates.png?w=840&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=220885a66801e1f784d4fa1007f09951 840w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Updates.png?w=1100&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=77176d50ff193f44f11014d67191764f 1100w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Updates.png?w=1650&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=5b8b9b4dd3b006a1855a78b8037e138d 1650w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Updates.png?w=2500&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=9cd95368d027607bd1402372b210797e 2500w">
    Embody Unreal Engine updates and community discussion.
  </Card>

  <Card title={<CustomCardTitle icon="pencil-line" title="Livepeer Onchain Builders" />} href="https://blog.livepeer.org/" img="https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Builders.png?fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=2c2ee7e7845030abce638337295e31f7" data-og-width="800" width="800" data-og-height="450" height="450" data-path="snippets/assets/media/heros/solutions/embody/Embody_Hero_Builders.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Builders.png?w=280&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=faec23c72d57e201f86bb4066e76958b 280w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Builders.png?w=560&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=2809e1220d495d72751a14cc79cbcee8 560w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Builders.png?w=840&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=1f13231cab7cb9dc97ac2924c5e65070 840w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Builders.png?w=1100&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=4451363e766e645f704decde37168ce8 1100w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Builders.png?w=1650&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=ab4e91dce258bba2fb11594f62d0b37f 1650w, https://mintcdn.com/na-36-merge-docs-v2-dev-draft-into-docs-v2-20260603/lFD5Mtu3hubBxvOS/snippets/assets/media/heros/solutions/embody/Embody_Hero_Builders.png?w=2500&fit=max&auto=format&n=lFD5Mtu3hubBxvOS&q=85&s=df5c38be635dbd0d6b69efb061304f1a 2500w">
    Livepeer ecosystem news and SPE updates.
  </Card>
</CardGroup>

<CustomDivider style={{margin: "-0.5rem 0 -2.5rem 0"}} />

## Get Started

Get started with Embody by trying the hosted app on the [Embody Website](https://embody.zone).

<Info>
  You need an NVIDIA GPU host and a Payments allowlist entry from the Embody team before starting. Contact the team via the [Embody Discord](https://discord.gg/gVmDMhYm) to get registered.
</Info>

<StyledSteps>
  <StyledStep title="Clone and run setup">
    ```bash theme={"theme":{"light":"github-light","dark":"dark-plus"}}
    git clone https://github.com/its-DeFine/Unreal_Vtuber.git
    cd Unreal_Vtuber
    sudo ./scripts/embody_cli.sh
    ```

    The CLI prompts for your payout wallet address (`ORCHESTRATOR_ADDRESS`) and your host's public IP. It writes `.env` and validates your Payments allowlist entry.
  </StyledStep>

  <StyledStep title="Pull the game image">
    ```bash theme={"theme":{"light":"github-light","dark":"dark-plus"}}
    sudo ./scripts/embody_cli.sh rollout
    ```

    Fetches, decrypts, and loads the encrypted Unreal Engine game image. Allow 10-20 minutes on first run.

    <Warning>
      If you see `denied` pulling `ghcr.io/.../embody-ue-ps:*`, your address is not yet allowlisted. Run `./scripts/embody_cli.sh allowlists fix` after the team confirms registration.
    </Warning>
  </StyledStep>

  <StyledStep title="Deploy the avatar cluster">
    ```bash theme={"theme":{"light":"github-light","dark":"dark-plus"}}
    sudo ./scripts/embody_cli.sh cluster deploy --auto --yes --pull missing
    ```

    `--auto` selects the number of avatar slots based on available GPU VRAM. The full stack starts: Pixel Streaming UE5 instance, health endpoint at `:9090`, signaling server at `:8080`.
  </StyledStep>

  <StyledStep title="Verify and connect">
    Check the node is healthy:

    ```bash theme={"theme":{"light":"github-light","dark":"dark-plus"}}
    curl http://localhost:9090/health
    curl http://localhost:9090/meta/gpu/stats
    ```

    Then open [embody.zone](https://embody.zone/) in Chrome or Edge. The matchmaker routes your session to the registered node. To connect directly to your instance, navigate to `http://<your-host-ip>:8080`.
  </StyledStep>
</StyledSteps>

<CustomDivider style={{margin: "-0.5rem 0 -2.5rem 0"}} />

## Embody Resources

<CenteredContainer maxWidth="fit-content">
  <BorderedBox style={{margin: "0 0 1rem 0", padding: "0.5rem 1rem 1rem 1rem"}} variant="accent">
    <SocialLinks iconColor="var(--text)" links={embodySocials} />
  </BorderedBox>
</CenteredContainer>

<CardGroup cols={2}>
  <Card title={<CustomCardTitle title="Embody" icon="globe"/>} href="https://embody.zone/" arrow>
    Website, product, and live avatar demos.
  </Card>

  <Card title={<CustomCardTitle title="Livepeer Forum" icon="comment-dots"/>} href="https://forum.livepeer.org/t/embody-unreal-engine-updates/3186" arrow>
    Technical updates, Unreal Engine integration, and community posts.
  </Card>
</CardGroup>
