Back to Articles

Bringing Clippy Back from the Dead: A TypeScript Resurrection Story

[ View on GitHub ]

Bringing Clippy Back from the Dead: A TypeScript Resurrection Story

Hook

The most hated UI element in computing history is making a comeback—and this time, it can talk to ChatGPT.

Context

Microsoft Office’s animated assistants—Clippy, Merlin, Rover, and friends—defined late-90s computing for millions of users before being retired amid widespread mockery. Yet decades later, these characters occupy a unique space in tech nostalgia: simultaneously despised and beloved, remembered as both intrusive annoyances and charming relics of a more optimistic internet era.

The original Clippy.JS library brought these assistants to the web using jQuery and pre-extracted sprite data, but it never escaped its legacy architecture. The pithings/clippy project is a ground-up TypeScript rewrite that modernizes the concept for 2024: ES modules, zero dependencies, CDN-ready, and with one killer feature that bridges past and present—the ability to stream text from async iterables, making it trivially easy to pipe modern LLM responses through a nostalgic UI paradigm.

Technical Insight

Rendering

Core

Client

import agent

sprite metadata

initialize

play/speak/moveTo

sequential actions

frame updates

text/stream

position data

CSS transforms

bubble HTML

User Code

Agent Modules

Clippy/Merlin/etc

initAgent

Action Queue

Animation Controller

DOM Renderer

CSS Sprite Animator

Speech Bubble

System architecture — auto-generated

ClippyJS implements a queue-based action system where animations, speech bubbles, and movements execute sequentially. Every action you trigger—whether play(), speak(), or moveTo()—gets pushed onto an internal queue that processes one operation at a time, coordinating sprite animations with DOM updates and user interactions.

The agent initialization process demonstrates the library’s modern approach. Instead of monolithic bundles, each character lives in its own module with pre-extracted sprite metadata and animation definitions:

import { initAgent } from "clippyjs";
import { Clippy } from "clippyjs/agents";

const agent = await initAgent(Clippy);
agent.show();
agent.speak("Hello! I'm Clippy, your virtual assistant.");

This tree-shakeable design means importing just Merlin doesn’t force you to download Clippy, Bonzi, and the other agents. For quick prototypes, the CDN option bypasses build tools entirely—paste a script tag and you’re running.

The real innovation emerges in the speakStream() method, which consumes async iterables and incrementally updates the speech bubble:

// Stream from an LLM API
const response = await fetch('/api/chat', {
  method: 'POST',
  body: JSON.stringify({ message: 'Tell me a joke' })
});

const reader = response.body
  .pipeThrough(new TextDecoderStream())
  .getReader();

const chunks = (async function* () {
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    yield value;
  }
})();

await agent.speakStream(chunks, { tts: true });

This pattern transforms Clippy from a static novelty into a surprisingly viable UI layer for AI interactions. The speech bubble updates as tokens arrive, and passing { tts: true } triggers the Web Speech API to vocalize the response using agent-specific voice personalities.

The gestureAt() method selects an appropriate pointing animation and orients the agent toward the target coordinates before playing the gesture sequence. The architecture appears to use sprite-based rendering, as the library works with pre-extracted sprite data from the original Microsoft Agent files using Double Agent.

Gotcha

Web Speech API support varies across browsers, which may affect the TTS functionality. If TTS is central to your use case, you’ll need to test across your target browsers.

The agent roster is fixed—you get the ten characters extracted from Microsoft’s original data (Clippy, Bonzi, F1, Genie, Genius, Links, Merlin, Peedy, Rocky, Rover) with their original animations, and that’s it. There’s no documented API for creating custom agents, modifying animations, or importing new sprite sheets. If you want a cyberpunk Clippy with neon animations, you’re building it yourself from scratch. The library preserves authenticity at the cost of extensibility, which fits its nostalgic mission but limits creative applications.

Verdict

Use ClippyJS if you’re building retro-themed projects, April Fools campaigns, creative AI chat interfaces, or tutorial systems where personality matters more than polish. It’s perfect for streaming LLM responses through a nostalgic UI, adding whimsy to developer tools, or any context where a talking paperclip genuinely enhances the experience. The CDN option makes experimentation trivial—you can have Clippy running in under five minutes. Skip it if you need production-grade accessibility, predictable cross-browser TTS behavior, custom character designs, or you’re working on serious enterprise applications where animated assistants might undermine credibility. This library optimizes for joy and nostalgia, not robustness and flexibility. That’s not a bug—it’s the entire point.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/automation/pithings-clippy.svg)](https://starlog.is/api/badge-click/automation/pithings-clippy)