Chapter 5: Extensions Ecosystem

March 2, 2026 · View on GitHub

Welcome to Chapter 5: Extensions Ecosystem. In this part of SillyTavern Tutorial: Advanced LLM Frontend for Power Users, you will build an intuitive mental model first, then move into concrete implementation details and practical production tradeoffs.

Discover and utilize SillyTavern's rich extension ecosystem to enhance your experience.

Overview

SillyTavern's extension system allows the community to add new features, integrations, and customizations. This chapter covers finding, installing, configuring, and troubleshooting extensions.

Extension Architecture

How Extensions Work

┌─────────────────────────────────────────────────────────────────┐
│                    Extension Architecture                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │                   SillyTavern Core                       │   │
│   │  ┌─────────────────┐  ┌─────────────────────────────┐   │   │
│   │  │ Extension API   │  │ Event System                │   │   │
│   │  │ - registerHook  │  │ - onMessage                 │   │   │
│   │  │ - addButton     │  │ - onCharacterLoad           │   │   │
│   │  │ - injectCSS     │  │ - onSettingsChange          │   │   │
│   │  └─────────────────┘  └─────────────────────────────┘   │   │
│   └─────────────────────────────────────────────────────────┘   │
│                               │                                  │
│           ┌───────────────────┼───────────────────┐              │
│           ▼                   ▼                   ▼              │
│   ┌───────────────┐   ┌───────────────┐   ┌───────────────┐     │
│   │   Extension   │   │   Extension   │   │   Extension   │     │
│   │   (Themes)    │   │   (TTS)       │   │   (Custom)    │     │
│   │               │   │               │   │               │     │
│   │  manifest.js  │   │  manifest.js  │   │  manifest.js  │     │
│   │  styles.css   │   │  index.js     │   │  index.js     │     │
│   │  settings.html│   │  settings.html│   │  ui.html      │     │
│   └───────────────┘   └───────────────┘   └───────────────┘     │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Extension Types

// Extension manifest structure
const extensionManifest = {
  // Basic metadata
  name: "My Extension",
  version: "1.0.0",
  description: "Adds amazing features",
  author: "Developer Name",

  // Type of extension
  type: "feature", // "theme", "integration", "utility", "feature"

  // Requirements
  requires: {
    minVersion: "1.10.0",
    extensions: ["extension-api-v2"],
    optional: ["tts-extension"]
  },

  // Entry points
  main: "index.js",
  settings: "settings.html",
  css: "styles.css",

  // Hooks and events
  hooks: [
    "onMessage",
    "onCharacterLoad",
    "onSettingsOpen"
  ],

  // UI elements
  ui: {
    button: {
      icon: "icon.svg",
      tooltip: "My Extension",
      position: "toolbar"
    },
    panel: {
      title: "Extension Panel",
      position: "sidebar"
    }
  }
};

Text-to-Speech (TTS)

// TTS extension configuration
const ttsConfig = {
  // Provider settings
  provider: "elevenlabs", // or "azure", "google", "system"

  // ElevenLabs specific
  elevenlabs: {
    apiKey: "your_api_key",
    voiceId: "voice_id",
    modelId: "eleven_monolingual_v1",
    stability: 0.5,
    similarityBoost: 0.75
  },

  // General settings
  autoPlay: true,
  skipCodeBlocks: true,
  skipEmotes: false,

  // Voice mapping per character
  characterVoices: {
    "Luna Starweaver": {
      provider: "elevenlabs",
      voiceId: "luna_voice_id",
      pitch: 1.1,
      rate: 0.9
    },
    "Dr. Sarah Chen": {
      provider: "azure",
      voiceId: "en-US-JennyNeural",
      pitch: 1.0,
      rate: 1.0
    }
  }
};

// Using TTS extension
const ttsExtension = {
  // Speak a message
  async speak(text, characterName) {
    const voiceConfig = ttsConfig.characterVoices[characterName] || ttsConfig;
    const audio = await generateSpeech(text, voiceConfig);
    await playAudio(audio);
  },

  // Queue multiple messages
  async queueSpeak(messages) {
    for (const msg of messages) {
      await this.speak(msg.text, msg.character);
    }
  },

  // Stop playback
  stop() {
    audioPlayer.stop();
    speechQueue.clear();
  }
};

Image Generation

// Image generation extension
const imageGenConfig = {
  provider: "stable-diffusion", // or "dalle", "midjourney-proxy"

  // Stable Diffusion settings
  stableDiffusion: {
    endpoint: "http://localhost:7860",
    model: "sd_xl_base_1.0",
    sampler: "DPM++ 2M Karras",
    steps: 20,
    cfgScale: 7,
    width: 512,
    height: 768
  },

  // Auto-generation settings
  autoGenerate: {
    enabled: true,
    triggers: ["*appears*", "*transforms*", "*reveals*"],
    cooldown: 60 // seconds between auto-generations
  },

  // Character-specific prompts
  characterPrompts: {
    "Luna Starweaver": {
      basePrompt: "elven woman, silver hair, violet eyes, purple robes, fantasy",
      negativePrompt: "modern clothing, technology"
    }
  }
};

// Image generation functions
const imageGenerator = {
  async generatePortrait(character, expression) {
    const charConfig = imageGenConfig.characterPrompts[character.name];
    const prompt = `${charConfig.basePrompt}, ${expression} expression, portrait`;

    return await callImageAPI({
      prompt,
      negativePrompt: charConfig.negativePrompt,
      ...imageGenConfig.stableDiffusion
    });
  },

  async generateScene(description) {
    const prompt = `${description}, detailed, high quality`;
    return await callImageAPI({
      prompt,
      width: 1024,
      height: 576 // Landscape for scenes
    });
  }
};

Expression/Sprite System

// Character expression extension
const expressionConfig = {
  enabled: true,

  // Expression detection
  detection: {
    method: "keyword", // or "sentiment", "llm"
    keywords: {
      happy: ["smiles", "laughs", "grins", "happy", "delighted"],
      sad: ["frowns", "tears", "sighs sadly", "disappointed"],
      angry: ["glares", "furious", "snaps", "angrily"],
      surprised: ["gasps", "eyes widen", "shocked", "startled"],
      thinking: ["ponders", "considers", "thoughtfully", "hmm"]
    }
  },

  // Sprite paths
  spritePaths: {
    "Luna Starweaver": {
      default: "characters/luna/default.png",
      happy: "characters/luna/happy.png",
      sad: "characters/luna/sad.png",
      angry: "characters/luna/angry.png",
      surprised: "characters/luna/surprised.png",
      thinking: "characters/luna/thinking.png"
    }
  },

  // Animation settings
  animation: {
    enabled: true,
    transitionDuration: 300, // ms
    blinkEnabled: true,
    blinkInterval: 4000 // ms
  }
};

// Expression detection and display
function detectAndShowExpression(message, characterName) {
  const keywords = expressionConfig.detection.keywords;
  const messageLower = message.toLowerCase();

  for (const [expression, triggers] of Object.entries(keywords)) {
    for (const trigger of triggers) {
      if (messageLower.includes(trigger)) {
        showSprite(characterName, expression);
        return;
      }
    }
  }

  // Default expression if no match
  showSprite(characterName, 'default');
}

World Info Manager

// Enhanced world info extension
const worldInfoManager = {
  // Advanced matching options
  matching: {
    caseSensitive: false,
    wholeWord: true,
    useRegex: false,
    fuzzyMatching: {
      enabled: true,
      threshold: 0.8
    }
  },

  // Entry relationships
  relationships: {
    // Parent-child entries
    hierarchy: {
      "Crystal Academy": ["Violet Tower", "Training Grounds", "Library"],
      "Magic System": ["Fire Magic", "Water Magic", "Earth Magic", "Air Magic"]
    },

    // Related entries (trigger together)
    related: {
      "Luna Starweaver": ["Violet Tower", "Crystal Academy"],
      "dark magic": ["forbidden spells", "corruption"]
    }
  },

  // Entry priority and ordering
  priority: {
    constant: 1000, // Always included
    character: 500, // Character-related info
    location: 400,  // Current location
    lore: 300,      // General world lore
    background: 200 // Low-priority background
  },

  // Token budget management
  tokenBudget: {
    total: 1500,
    perEntry: 300,
    reserveForChat: 500
  }
};

Installing Extensions

From the Built-in Manager

// Using the extension manager
const extensionManager = {
  // List available extensions
  async listAvailable() {
    const response = await fetch('https://api.sillytavern.app/extensions');
    return response.json();
  },

  // Install extension
  async install(extensionId) {
    const extension = await this.fetchExtension(extensionId);

    // Validate manifest
    if (!this.validateManifest(extension.manifest)) {
      throw new Error('Invalid extension manifest');
    }

    // Check requirements
    if (!this.checkRequirements(extension.manifest.requires)) {
      throw new Error('Requirements not met');
    }

    // Extract to extensions folder
    await extractExtension(extension, './extensions/');

    // Register with core
    await this.registerExtension(extension.manifest);

    return { success: true, extension: extension.manifest };
  },

  // Update extension
  async update(extensionId) {
    const current = this.getInstalled(extensionId);
    const latest = await this.fetchLatest(extensionId);

    if (this.compareVersions(current.version, latest.version) < 0) {
      await this.install(extensionId);
      return { updated: true, newVersion: latest.version };
    }

    return { updated: false };
  }
};

Manual Installation

# Clone extension to extensions folder
cd SillyTavern/public/extensions
git clone https://github.com/user/extension-name

# Or download and extract
wget https://example.com/extension.zip
unzip extension.zip -d extension-name/

# Restart SillyTavern to load
npm run start

Configuring Extensions

Settings Interface

// Extension settings template
const extensionSettings = {
  // Settings schema
  schema: {
    enabled: {
      type: 'boolean',
      default: true,
      label: 'Enable Extension'
    },
    apiKey: {
      type: 'string',
      default: '',
      label: 'API Key',
      secret: true
    },
    quality: {
      type: 'select',
      options: ['low', 'medium', 'high'],
      default: 'medium',
      label: 'Quality Level'
    },
    volume: {
      type: 'range',
      min: 0,
      max: 100,
      default: 80,
      label: 'Volume'
    }
  },

  // Load settings
  load() {
    const saved = localStorage.getItem('ext_myextension');
    return saved ? JSON.parse(saved) : this.getDefaults();
  },

  // Save settings
  save(settings) {
    localStorage.setItem('ext_myextension', JSON.stringify(settings));
    this.emit('settingsChanged', settings);
  },

  // Get default values
  getDefaults() {
    const defaults = {};
    for (const [key, config] of Object.entries(this.schema)) {
      defaults[key] = config.default;
    }
    return defaults;
  }
};

Per-Character Settings

// Character-specific extension overrides
const characterOverrides = {
  // Get settings for character
  getForCharacter(characterId, extensionId) {
    const character = getCharacter(characterId);
    const globalSettings = getExtensionSettings(extensionId);
    const characterSettings = character.extensionData?.[extensionId] || {};

    // Merge with character settings taking priority
    return { ...globalSettings, ...characterSettings };
  },

  // Save character override
  setForCharacter(characterId, extensionId, settings) {
    const character = getCharacter(characterId);

    if (!character.extensionData) {
      character.extensionData = {};
    }

    character.extensionData[extensionId] = settings;
    saveCharacter(character);
  }
};

Themes and Visual Customization

Theme Structure

/* Theme CSS example */
:root {
  /* Color palette */
  --primary-color: #6b5ce7;
  --secondary-color: #a29bfe;
  --background-color: #1a1a2e;
  --surface-color: #16213e;
  --text-color: #edf2f4;
  --text-muted: #8d99ae;

  /* Typography */
  --font-family: 'Inter', sans-serif;
  --font-size-base: 14px;
  --line-height: 1.6;

  /* Spacing */
  --spacing-unit: 8px;
  --border-radius: 8px;

  /* Chat specific */
  --user-message-bg: var(--primary-color);
  --bot-message-bg: var(--surface-color);
  --message-padding: calc(var(--spacing-unit) * 2);
}

/* Chat messages */
.message-user {
  background: var(--user-message-bg);
  color: var(--text-color);
  padding: var(--message-padding);
  border-radius: var(--border-radius);
  margin-left: 20%;
}

.message-bot {
  background: var(--bot-message-bg);
  color: var(--text-color);
  padding: var(--message-padding);
  border-radius: var(--border-radius);
  margin-right: 20%;
}

/* Character avatar */
.character-avatar {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 2px solid var(--primary-color);
}

/* Input area */
.chat-input {
  background: var(--surface-color);
  border: 1px solid var(--primary-color);
  color: var(--text-color);
  padding: var(--message-padding);
  border-radius: var(--border-radius);
}

Creating Custom Themes

// Theme builder
const themeBuilder = {
  // Base theme template
  baseTemplate: {
    name: "My Custom Theme",
    version: "1.0.0",
    author: "Your Name",

    colors: {
      primary: "#6b5ce7",
      secondary: "#a29bfe",
      background: "#1a1a2e",
      surface: "#16213e",
      text: "#edf2f4",
      textMuted: "#8d99ae",
      success: "#00b894",
      warning: "#fdcb6e",
      error: "#e74c3c"
    },

    fonts: {
      main: "'Inter', sans-serif",
      mono: "'Fira Code', monospace"
    },

    effects: {
      blurAmount: "10px",
      shadowIntensity: "0.3",
      borderRadius: "8px"
    }
  },

  // Generate CSS from theme config
  generateCSS(theme) {
    return `
:root {
  --primary-color: ${theme.colors.primary};
  --secondary-color: ${theme.colors.secondary};
  --background-color: ${theme.colors.background};
  --surface-color: ${theme.colors.surface};
  --text-color: ${theme.colors.text};
  --text-muted: ${theme.colors.textMuted};
  --font-family: ${theme.fonts.main};
  --font-mono: ${theme.fonts.mono};
  --blur-amount: ${theme.effects.blurAmount};
  --border-radius: ${theme.effects.borderRadius};
}`;
  },

  // Apply theme
  apply(theme) {
    const css = this.generateCSS(theme);
    const styleEl = document.createElement('style');
    styleEl.textContent = css;
    styleEl.id = 'custom-theme';

    // Remove existing custom theme
    document.getElementById('custom-theme')?.remove();
    document.head.appendChild(styleEl);
  }
};

Troubleshooting Extensions

Common Issues

// Extension diagnostics
const extensionDiagnostics = {
  // Check extension health
  async checkHealth(extensionId) {
    const results = {
      loaded: false,
      initialized: false,
      errors: [],
      warnings: []
    };

    // Check if loaded
    const extension = window.extensions?.[extensionId];
    if (!extension) {
      results.errors.push('Extension not loaded');
      return results;
    }
    results.loaded = true;

    // Check if initialized
    if (extension.initialized) {
      results.initialized = true;
    } else {
      results.warnings.push('Extension loaded but not initialized');
    }

    // Check dependencies
    const manifest = extension.manifest;
    if (manifest.requires) {
      for (const dep of manifest.requires.extensions || []) {
        if (!window.extensions?.[dep]) {
          results.errors.push(`Missing dependency: ${dep}`);
        }
      }
    }

    // Check for version conflicts
    if (manifest.requires?.minVersion) {
      const currentVersion = window.SillyTavern?.version;
      if (this.compareVersions(currentVersion, manifest.requires.minVersion) < 0) {
        results.errors.push(
          `Requires SillyTavern ${manifest.requires.minVersion}, current: ${currentVersion}`
        );
      }
    }

    return results;
  },

  // Compare semantic versions
  compareVersions(a, b) {
    const partsA = a.split('.').map(Number);
    const partsB = b.split('.').map(Number);

    for (let i = 0; i < 3; i++) {
      if (partsA[i] > partsB[i]) return 1;
      if (partsA[i] < partsB[i]) return -1;
    }
    return 0;
  }
};

Summary

In this chapter, you've learned:

  • Extension Architecture: How extensions integrate with SillyTavern
  • Popular Extensions: TTS, image generation, expressions, and more
  • Installation: Installing from manager or manually
  • Configuration: Setting up extensions and per-character overrides
  • Themes: Creating and customizing visual themes
  • Troubleshooting: Diagnosing and fixing extension issues

Key Takeaways

  1. Extensions enhance functionality: Explore the ecosystem
  2. Check compatibility: Ensure extensions match your version
  3. Backup before installing: Extensions can affect data
  4. Per-character settings: Customize per character
  5. Themes are extensions: Customize visually with CSS

Next Steps

Now that you understand the extension ecosystem, let's explore setting up multiple AI backends in Chapter 6: Multi-Model Setup.


Ready for Chapter 6? Multi-Model Setup

Generated for Awesome Code Docs

What Problem Does This Solve?

Most teams struggle here because the hard part is not writing more code, but deciding clear boundaries for extension, color, theme so behavior stays predictable as complexity grows.

In practical terms, this chapter helps you avoid three common failures:

  • coupling core logic too tightly to one implementation path
  • missing the handoff boundaries between setup, execution, and validation
  • shipping changes without clear rollback or observability strategy

After working through this chapter, you should be able to reason about Chapter 5: Extensions Ecosystem as an operating subsystem inside SillyTavern Tutorial: Advanced LLM Frontend for Power Users, with explicit contracts for inputs, state transitions, and outputs.

Use the implementation notes around settings, manifest, character as your checklist when adapting these patterns to your own repository.

How it Works Under the Hood

Under the hood, Chapter 5: Extensions Ecosystem usually follows a repeatable control path:

  1. Context bootstrap: initialize runtime config and prerequisites for extension.
  2. Input normalization: shape incoming data so color receives stable contracts.
  3. Core execution: run the main logic branch and propagate intermediate state through theme.
  4. Policy and safety checks: enforce limits, auth scopes, and failure boundaries.
  5. Output composition: return canonical result payloads for downstream consumers.
  6. Operational telemetry: emit logs/metrics needed for debugging and performance tuning.

When debugging, walk this sequence in order and confirm each stage has explicit success/failure conditions.

Source Walkthrough

Use the following upstream sources to verify implementation details while reading this chapter:

Suggested trace strategy:

  • search upstream code for extension and color to map concrete implementation paths
  • compare docs claims against actual runtime/config code before reusing patterns in production

Chapter Connections