mascot-integrator
Integrate the emotive-mascot into new use cases, applications, and frameworks. Use when adding mascot to a new page, creating custom interactions, or setting up framework-specific implementations (React, Vue, vanilla JS).
$ Installer
git clone https://github.com/joshtol/emotive-engine /tmp/emotive-engine && cp -r /tmp/emotive-engine/skills/mascot-integrator ~/.claude/skills/emotive-engine// tip: Run this command in your terminal to install the skill
name: mascot-integrator description: Integrate the emotive-mascot into new use cases, applications, and frameworks. Use when adding mascot to a new page, creating custom interactions, or setting up framework-specific implementations (React, Vue, vanilla JS). trigger: integrate, add mascot, setup, implementation, use case, framework
Mascot Integrator
You are an expert in integrating the emotive-mascot engine into various applications, frameworks, and use cases.
When to Use This Skill
- Adding mascot to a new use case or page
- Setting up mascot in different frameworks (React, Vue, Svelte, vanilla JS)
- Creating custom interaction patterns
- Configuring mascot for specific contexts
- Implementing LLM-driven emotion responses
- Troubleshooting integration issues
Quick Start Patterns
Vanilla JavaScript
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/@joshtol/emotive-engine@latest"></script>
</head>
<body>
<canvas id="mascot-canvas"></canvas>
<script>
const mascot = new EmotiveMascot({
containerId: 'mascot-canvas',
width: 400,
height: 400,
initialEmotion: 'calm'
})
await mascot.initialize()
await mascot.transitionTo('joy')
</script>
</body>
</html>
React (Next.js App Router)
'use client';
import { useEffect, useRef, useState } from 'react';
export default function MascotDemo() {
const canvasRef = useRef<HTMLCanvasElement>(null);
const mascotRef = useRef<any>(null);
const [isClient, setIsClient] = useState(false);
useEffect(() => {
setIsClient(true);
}, []);
useEffect(() => {
if (!isClient || !canvasRef.current) return;
const initMascot = async () => {
const { EmotiveMascot } = await import('@joshtol/emotive-engine');
mascotRef.current = new EmotiveMascot({
canvas: canvasRef.current,
width: 400,
height: 400,
initialEmotion: 'calm',
enableGazeTracking: true,
});
await mascotRef.current.initialize();
};
initMascot();
return () => {
mascotRef.current?.destroy();
};
}, [isClient]);
const handleEmotionChange = async (emotion: string) => {
await mascotRef.current?.transitionTo(emotion, { duration: 1000 });
};
if (!isClient) return null;
return (
<div>
<canvas ref={canvasRef} />
<button onClick={() => handleEmotionChange('joy')}>Joy</button>
<button onClick={() => handleEmotionChange('excitement')}>
Excitement
</button>
</div>
);
}
Vue 3
<template>
<div>
<canvas ref="mascotCanvas"></canvas>
<button @click="changeEmotion('joy')">Joy</button>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const mascotCanvas = ref(null);
let mascot = null;
onMounted(async () => {
const { EmotiveMascot } = await import('@joshtol/emotive-engine');
mascot = new EmotiveMascot({
canvas: mascotCanvas.value,
width: 400,
height: 400,
initialEmotion: 'calm',
});
await mascot.initialize();
});
onUnmounted(() => {
mascot?.destroy();
});
const changeEmotion = async emotion => {
await mascot?.transitionTo(emotion);
};
</script>
Use Case Patterns
1. Retail Checkout Assistant
Context: Shopping cart, payment processing, item scanning Emotions: calm → anticipation (scanning) → joy (success) → concern (error)
const mascot = new EmotiveMascot({
containerId: 'checkout-mascot',
initialEmotion: 'calm',
enableGazeTracking: true,
scrollReactive: false, // Fixed position at checkout
});
// Item scanned successfully
async function onItemScanned() {
await mascot.playPerformance('itemScanned', {
steps: [
{ emotion: 'anticipation', duration: 300 },
{ emotion: 'joy', duration: 800, gesture: 'bounce' },
],
});
}
// Payment processing
async function onPaymentProcessing() {
await mascot.transitionTo('anticipation', { duration: 500 });
// Show loading state
}
// Payment success
async function onPaymentSuccess() {
await mascot.playPerformance('celebration');
}
// Error occurred
async function onError(errorType) {
await mascot.transitionTo('concern', { duration: 800 });
}
2. Smart Home Dashboard
Context: Device control, status monitoring, voice commands Emotions: calm → focus (listening) → joy (command success)
const mascot = new EmotiveMascot({
containerId: 'smarthome-mascot',
initialEmotion: 'calm',
enableGazeTracking: true,
scrollReactive: true,
audioEnabled: true, // Enable audio feedback
});
// Voice command started
async function onVoiceStart() {
await mascot.transitionTo('focus', { duration: 400 });
}
// Command recognized
async function onCommandRecognized(command) {
await mascot.transitionTo('anticipation', { duration: 300 });
}
// Device controlled successfully
async function onDeviceControlled(device) {
await mascot.playPerformance('success', {
steps: [
{ emotion: 'joy', duration: 600, gesture: 'pulse' },
{ emotion: 'calm', duration: 800 },
],
});
}
3. Healthcare Patient Intake
Context: Form filling, health questions, appointment scheduling Emotions: calm → empathy → reassurance
const mascot = new EmotiveMascot({
containerId: 'patient-mascot',
initialEmotion: 'calm',
enableGazeTracking: true,
scrollReactive: false,
});
// Sensitive question
async function onSensitiveQuestion() {
await mascot.transitionTo('empathy', { duration: 1000 });
}
// Form validation error
async function onValidationError() {
await mascot.transitionTo('concern', { duration: 600 });
}
// Form submitted
async function onFormSubmitted() {
await mascot.playPerformance('reassurance', {
steps: [
{ emotion: 'gratitude', duration: 800 },
{ emotion: 'calm', duration: 600 },
],
});
}
4. Education / Learning Platform
Context: Quiz, lessons, progress tracking Emotions: calm → encouragement → celebration (correct) → empathy (incorrect)
const mascot = new EmotiveMascot({
containerId: 'learning-mascot',
initialEmotion: 'calm',
enableGazeTracking: true,
});
// Correct answer
async function onCorrectAnswer() {
await mascot.playPerformance('celebration', {
steps: [
{ emotion: 'joy', duration: 600, gesture: 'bounce' },
{ emotion: 'pride', duration: 800, gesture: 'shimmer' },
],
});
}
// Incorrect answer
async function onIncorrectAnswer() {
await mascot.playPerformance('encouragement', {
steps: [
{ emotion: 'empathy', duration: 600 },
{ emotion: 'encouragement', duration: 800 },
],
});
}
// Lesson completed
async function onLessonComplete() {
await mascot.playPerformance('achievement');
}
LLM Integration Pattern
Connect mascot to Claude, GPT, or other LLMs for sentiment-driven emotions:
import { LLMEmotionPlugin } from '@joshtol/emotive-engine';
const mascot = new EmotiveMascot({
containerId: 'ai-mascot',
initialEmotion: 'calm',
plugins: [
new LLMEmotionPlugin({
provider: 'anthropic', // or 'openai'
apiKey: process.env.ANTHROPIC_API_KEY,
autoDetect: true, // Automatically detect sentiment
}),
],
});
// Handle LLM response
async function onLLMResponse(message) {
// Plugin automatically detects sentiment and transitions emotion
await mascot.handleLLMMessage(message);
}
// Manual sentiment override
async function onUserMessage(message) {
if (message.includes('error') || message.includes('problem')) {
await mascot.transitionTo('concern', { duration: 800 });
} else if (message.includes('thank') || message.includes('great')) {
await mascot.transitionTo('gratitude', { duration: 600 });
}
}
Configuration Options
Essential Options
{
// Container (choose one)
containerId: 'my-canvas', // ID of canvas element
canvas: canvasElement, // Direct canvas reference
// Dimensions
width: 400, // Canvas width
height: 400, // Canvas height
// Initial state
initialEmotion: 'calm', // Starting emotion
// Interactivity
enableGazeTracking: true, // Follow mouse/touch
scrollReactive: true, // React to page scroll
clickReactive: true, // React to clicks
// Performance
targetFPS: 60, // Target frame rate
pixelRatio: window.devicePixelRatio, // Display quality
// Features
audioEnabled: false, // Audio synthesis
plugins: [] // Additional plugins
}
Advanced Options
{
// Accessibility
ariaLabel: 'Interactive mascot',
reducedMotion: false, // Respect prefers-reduced-motion
// Debug
debug: false, // Show FPS and debug info
// Custom behaviors
onEmotionChange: (emotion) => {
console.log('Emotion changed to:', emotion)
},
onGestureComplete: (gesture) => {
console.log('Gesture completed:', gesture)
},
onPerformanceComplete: (performance) => {
console.log('Performance completed:', performance)
}
}
Common Integration Patterns
Fixed Position Mascot
#mascot-canvas {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 1000;
border-radius: 50%;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
}
Scroll-Reactive Hero Section
const mascot = new EmotiveMascot({
containerId: 'hero-mascot',
scrollReactive: true,
scrollConfig: {
minScroll: 0,
maxScroll: 1000,
emotionSequence: [
{ scrollY: 0, emotion: 'calm' },
{ scrollY: 300, emotion: 'curiosity' },
{ scrollY: 600, emotion: 'excitement' },
{ scrollY: 1000, emotion: 'joy' },
],
},
});
Mobile Responsive
const isMobile = window.innerWidth < 768;
const mascot = new EmotiveMascot({
containerId: 'mascot',
width: isMobile ? 300 : 500,
height: isMobile ? 300 : 500,
targetFPS: isMobile ? 30 : 60, // Lower FPS on mobile
enableGazeTracking: !isMobile, // Disable on mobile for performance
});
Troubleshooting
Mascot not rendering
- Verify canvas element exists in DOM
- Check canvas has width/height (not 0x0)
- Ensure
initialize()is called after construction - Check browser console for errors
Performance issues
- Reduce particle count in emotion configs
- Lower targetFPS to 30
- Disable gaze tracking on mobile
- Check for other animation conflicts
Emotions not changing
- Verify emotion name exists in emotion config
- Check transition is awaited (async)
- Ensure previous transition completed
- Check for JavaScript errors blocking execution
Framework-specific issues
React: Canvas disappears on re-render
- Use
useReffor canvas element - Don't recreate mascot on every render
- Clean up with
destroy()in cleanup function
Vue: Mascot not initializing
- Use
onMountedhook - Check canvas ref is properly bound
- Use
nextTick()if needed
Next.js: SSR errors
- Use
'use client'directive - Check for
windowbefore initializing - Use dynamic imports for mascot library
Key Files
- Core Engine:
src/core/EmotiveMascot.js - LLM Plugin:
src/plugins/LLMEmotionPlugin.js - Gaze Tracking:
src/core/GazeTrackingEngine.js - Scroll Reactive:
src/core/ScrollReactiveEngine.js - Example Use Cases:
site/src/app/use-cases/
Testing Integration
// Test basic initialization
const mascot = new EmotiveMascot({ containerId: 'test-canvas' });
await mascot.initialize();
console.assert(mascot.isInitialized, 'Mascot should be initialized');
// Test emotion transition
await mascot.transitionTo('joy');
console.assert(mascot.currentEmotion === 'joy', 'Should transition to joy');
// Test gesture
await mascot.playGesture('wave');
console.assert(mascot.isGesturePlaying, 'Gesture should be playing');
// Clean up
mascot.destroy();
Resources
Repository
