Virtual avatar engineering
Skill meyverick/agy-skills/skills/virtual-avatar-engineering
Engineers real-time web-based virtual avatars, computer vision pipelines, and high-performance interactive graphics using Svelte 5 and SvelteKit. Orchestrates local ML execution via MediaPipe Tasks Vision, WebAssembly optimization, mathematical animations (LERP, EAR), and Web Worker isolation to drive seamless WebGL/Canvas overlays without dropping frames.From its SKILL.md
npx -y skills add meyverick/agy-skills --skill virtual-avatar-engineeringAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its file declares
Copied from the file, not written here
The file declares its own license as Apache-2.0. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
7.8 KB, ~1.8k tokens by cl100k_base, as published. Nobody here has run it
Virtual Avatar Engineering & Real-Time Computer Vision
This skill outlines the process of building high-performance, real-time virtual avatars and computer vision pipelines using Svelte 5 and SvelteKit. It focuses on isolating heavy Machine Learning (ML) inference from the main UI thread, applying mathematical biomechanical calculations, and ensuring stable 60+ FPS broadcast-quality rendering.
Activation Criteria
Use this skill when:
- Developing web-based VTuber avatars or Face/Hand/Holistic tracking pipelines using MediaPipe.
- Implementing low-latency device streams (
getUserMedia) and processing raw video frames. - Integrating 2D (Canvas), 2.5D (Live2D), or 3D (VRM/Three.js) avatars driven by real-time sensor data.
- Isolating heavy computational workloads using Web Workers and WebAssembly.
1. Computer Vision & WebWorker Isolation
Computer vision inference (like MediaPipe) blocks the main thread, causing severe UI jank and dropped frames in avatar rendering.
- Worker Isolation: Always execute ML models and computer vision pipelines inside a dedicated Web Worker. The main thread should only handle the Svelte UI and the WebGL/Canvas rendering loop.
- Zero-Copy Transfers: Use
ImageBitmapandpostMessagewith Transferable Objects to send video frames to the Web Worker without duplicating memory. - Execution Delegates: Explicitly configure execution delegates (CPU vs. GPU) depending on the environment. For broadcast setups (e.g., OBS Studio browser sources), CPU delegates often perform better because OBS heavily saturates the GPU.
2. Mathematical Animation & Biomechanics
Raw tracking data from computer vision models is inherently noisy and jittery. It must be smoothed before driving avatar rigs.
- Linear Interpolation (LERP): Apply LERP to all positional data (x, y, z coordinates) and rotational data (quaternions/Eulers) to smooth out camera micro-jitters.
// LERP Function const lerp = (start, end, amt) => (1 - amt) * start + amt * end; - Biomechanical Metrics: Compute relational metrics rather than absolute coordinates to handle varying face distances.
- Eye Aspect Ratio (EAR): Calculate EAR to determine if an eye is open or closed, triggering blink animations.
- Mouth Open Factor (MOF): Calculate the vertical distance between inner lip landmarks divided by face height to drive the avatar's jaw bone.
- Geometric Clamping: Clamp tracking metrics to strict upper and lower bounds to prevent avatars from contorting unnaturally when landmarks fail.
3. Svelte 5 Rendering Architecture
Separate the reactive UI state from the 60+ FPS rendering context.
- Runes Protocol: Use Svelte 5
$stateand$derivedstrictly for UI configurations (e.g., selecting a microphone, toggling debug overlays, changing avatars). - Decoupled Rendering: The Canvas or WebGL context (Three.js/PixiJS) must be updated inside a pure
requestAnimationFrameloop. Do not bind high-frequency avatar bone transforms directly to Svelte reactive state variables. - Immutable Config Sync: When UI configuration changes (e.g., user selects a new accessory), sync the data to the render loop using immutable references to prevent deep-diffing overhead.
4. Device Streams & Defensive Programming
Hardware access is highly volatile. The application must gracefully handle stream failures and permission denials.
- Permission Handling: Wrap
navigator.mediaDevices.getUserMediaintry/catchblocks. Provide clear Svelte UI fallback states if permissions are denied or hardware is missing. - Stream Cleanup: If a component unmounts or a user switches cameras, you must explicitly call
track.stop()on all MediaStream tracks. - WASM Teardown: MediaPipe WASM instances consume significant memory. Ensure the Web Worker receives a teardown message to run
.close()on tasks when the avatar session ends.
5. Self-Validation Protocol
Before deploying or committing a virtual avatar component, verify:
- Thread Safety: Is the MediaPipe inference fully isolated in a Web Worker?
- Memory Leaks: Does the
$effectteardown stop the video stream, cancel therequestAnimationFrame, and terminate the Web Worker? - Smoothing: Is raw tracking data being smoothed via LERP or moving averages before being applied to the skeleton?
- Fallback UI: Is there a reactive Svelte UI state (
$state(error)) indicating when a camera is inaccessible or the ML model failed to load?
Reference Example
Below is a conceptual Svelte 5 component demonstrating stream initialization, worker isolation, and decoupled rendering.
<script lang="ts">
import { onDestroy } from 'svelte';
let videoElement: HTMLVideoElement;
let canvasElement: HTMLCanvasElement;
let streamStatus = $state<'loading' | 'active' | 'error'>('loading');
let errorMessage = $state('');
let worker: Worker;
let rAFId: number;
let mediaStream: MediaStream | null = null;
// Avatar State (Non-reactive for performance)
const avatarState = {
headYaw: 0,
mouthOpen: 0,
lerpFactor: 0.2
};
$effect(() => {
// 1. Initialize Web Worker for ML isolation
worker = new Worker(new URL('./vision-worker.ts', import.meta.url), { type: 'module' });
worker.onmessage = (e) => {
// Receive raw data, apply LERP smoothing
const { rawYaw, rawMouth } = e.data;
avatarState.headYaw = lerp(avatarState.headYaw, rawYaw, avatarState.lerpFactor);
avatarState.mouthOpen = lerp(avatarState.mouthOpen, rawMouth, avatarState.lerpFactor);
};
// 2. Request Camera
navigator.mediaDevices.getUserMedia({ video: { width: 640, height: 480 }, audio: false })
.then((stream) => {
mediaStream = stream;
videoElement.srcObject = stream;
videoElement.play();
streamStatus = 'active';
// 3. Start render loop
startRenderLoop();
})
.catch((err) => {
streamStatus = 'error';
errorMessage = err.message;
});
// 4. Defensive Cleanup
return () => {
if (rAFId) cancelAnimationFrame(rAFId);
if (worker) worker.terminate();
if (mediaStream) {
mediaStream.getTracks().forEach(track => track.stop());
}
};
});
function startRenderLoop() {
const ctx = canvasElement.getContext('2d');
const loop = () => {
if (videoElement.readyState >= 2 && streamStatus === 'active') {
// Send frame to worker (simplified concept, usually draw to OffscreenCanvas)
worker.postMessage({ type: 'PROCESS_FRAME' });
// Render Avatar to Canvas based on smoothed avatarState
ctx.clearRect(0, 0, canvasElement.width, canvasElement.height);
ctx.fillStyle = 'blue'; // Example mouth
ctx.fillRect(150, 200, 100, avatarState.mouthOpen * 50);
}
rAFId = requestAnimationFrame(loop);
};
loop();
}
const lerp = (start: number, end: number, amt: number) => (1 - amt) * start + amt * end;
</script>
<div class="avatar-container">
{#if streamStatus === 'loading'}
<p>Initializing Camera and ML Models...</p>
{:else if streamStatus === 'error'}
<p class="error">Camera Error: {errorMessage}</p>
{/if}
<!-- Hidden video element for stream source -->
<video bind:this={videoElement} style="display: none;" playsinline muted></video>
<!-- Avatar Render Target -->
<canvas bind:this={canvasElement} width="640" height="480"></canvas>
</div>
<style>
.avatar-container { position: relative; }
.error { color: red; }
</style>
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 0 of the 12 instructions most performance cost skills give in ~1.8k tokens
Counted across 803 of the 1,058 authors here whose files we hold, read 2026-08-07
- Keep skill files under 500 lines or tokensin 82 of 803, across 16 files
- Use imperative form in instructionsin 80 of 803, across 9 files
- Draft assertions while test runs are in progressin 75 of 803, across 9 files
- Create two to three realistic test promptsin 74 of 803, across 9 files
- Write skill descriptions to be pushyin 72 of 803, across 7 files
- Save test cases to evals JSONin 72 of 803, across 6 files
- Ask questions about edge cases and input formatsin 72 of 803, across 7 files
- Save timing data immediately when runs completein 70 of 803, across 5 files
- Include all trigger conditions in the skill descriptionin 69 of 803, across 3 files
- Launch all test runs in a single turn or simultaneouslyin 69 of 803, across 3 files
- Capture intent before writing a skillin 67 of 803, across 1 file
- Import directly instead of barrel filesin 52 of 803, across 15 files
Said here and by no other author read
- Execute ML models inside a dedicated Web Worker
- Use zero-copy transfers for video frames
- Apply linear interpolation to tracking data
- Compute relational metrics instead of absolute coordinates
- Clamp tracking metrics to strict bounds
- Update canvas inside a requestAnimationFrame loop
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.