opencode/skills/explorable-explanations/SKILL.md
Build single-file interactive HTML explanations that teach concepts through experimentation — sliders, animations, step-through controls, and multiple visual representations. Use when the user explicitly asks to build an explorable explanation, interactive explanation, or animated walkthrough of a concept.
npx skillsauth add third774/dotfiles explorable-explanationsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Build interactive single-file HTML pages that teach concepts through direct manipulation and experimentation, not passive reading. The reader should develop working intuition by playing with the system, not by being told how it works.
Core belief: People learn through experimentation. A slider that lets you feel how frequency affects pitch teaches more than a paragraph explaining it.
Match interaction complexity to concept complexity. A concept that needs one slider and one visualization should not ship with a transport bar, scroll-driven progression, and multiple linked representations. Every control, view, and animation frame you add costs the reader attention — spend only what the concept demands.
Before building, ask: what is the minimum interaction that gives the reader the intuition they need? Start there. Add more only when a simpler version falls short.
Draw from these layers as the concept demands. Not every explorable needs all six — skip steps that don't earn their weight. The order is a sensible default, not a mandate.
Start with something concrete and familiar. Before explaining how a hash table works, show a phonebook. Before explaining Fourier transforms, play a sound.
Introduce complexity incrementally. Each new interactive control SHOULD teach exactly one thing.
Every input change MUST produce an immediately visible output change.
Some concepts click when the reader sees them from two angles at once — a wave as motion and as a graph, a matrix transform as numbers and as geometry. When this applies, linked representations are extremely effective:
Not every concept benefits from this. An explorable about easing functions, color mixing, or grid layout doesn't need a second view — the primary visualization is the explanation. Add a second representation only when it reveals a relationship that one view alone cannot.
The reader SHOULD be able to push parameters to extremes.
Technical terminology SHOULD appear after the reader already has intuition for the concept.
Output MUST be a single .html file with no build step. Use Tailwind CSS via CDN for styling.
Start with vanilla JS. Event listeners, requestAnimationFrame, and direct DOM updates handle most explorables cleanly. Reach for Vue 3 (via CDN) only when the complexity genuinely warrants it — roughly when you have 5+ reactive bindings that depend on each other, or component-like sections that benefit from Vue's template syntax.
Vanilla baseline:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Explorable: [Topic]</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
/* Fallback CSS only when Tailwind utilities are insufficient */
</style>
</head>
<body>
<div id="app">
<!-- Template here -->
</div>
<script>
// Direct DOM refs, event listeners, requestAnimationFrame
</script>
</body>
</html>
When Vue is warranted, add the CDN script and use the Composition API:
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script>
const { createApp, ref, computed, onMounted, onUnmounted, watch } = Vue
createApp({
setup() {
// Reactive state, computed properties, animation loops
return { /* template bindings */ }
}
}).mount('#app')
</script>
<style> only as a fallback when a style cannot be expressed cleanly with available utility classes.Hard rule: Tailwind arbitrary values ([...]) are NOT available in this single-file HTML + no-build-step + CDN setup and MUST NOT be used.
Reference: https://tailwindcss.com/docs/adding-custom-styles#using-arbitrary-values
| Use SVG when | Use Canvas when | |---|---| | < 500 elements | > 500 elements | | Need DOM events on individual elements | Need pixel-level control | | Mostly shapes, lines, text | Particle systems, complex animation | | Interactivity on individual elements | Performance-critical rendering |
Default to SVG. It's declarative, inspectable in devtools, and handles most explorable needs. It also composes well with Vue's template syntax if you're using Vue.
For animated explorables, use requestAnimationFrame with elapsed-time-based updates (not frame-counting):
const elapsed = ref(0)
const playing = ref(false)
const speed = ref(1)
let lastTime = null
let frameId = null
function tick(now) {
if (lastTime != null) {
elapsed.value += (now - lastTime) * speed.value
}
lastTime = now
frameId = requestAnimationFrame(tick)
}
function togglePlay() {
if (playing.value) {
cancelAnimationFrame(frameId)
lastTime = null
playing.value = false
} else {
playing.value = true
frameId = requestAnimationFrame(tick)
}
}
// Step forward one frame (~16ms) while paused
function step() {
if (!playing.value) {
elapsed.value += 16 * speed.value
}
}
onUnmounted(() => cancelAnimationFrame(frameId))
When a discrete parameter change should animate smoothly (e.g., morphing between shapes), use linear interpolation driven by requestAnimationFrame:
function lerp(a, b, t) {
return a + (b - a) * Math.min(1, Math.max(0, t))
}
For spring-like smoothing on slider values, apply exponential easing each frame:
// In the animation loop:
displayValue.value += (targetValue.value - displayValue.value) * 0.1
Animated explorables need some way for the reader to control time. Pick from this menu based on what the concept requires — don't include controls the reader won't use:
A simple looping animation might only need play/pause. A sorting algorithm walkthrough might need step + reset. Let the concept decide.
For long-form explorables that tell a story:
IntersectionObserver to trigger state changes as sections enter the viewportThis structure works well for long-form explorables that build up a complex concept over many steps. For simpler concepts, a single viewport with everything visible is often better — the reader can cross-reference controls and output without losing context to scrolling.
A typical explorable layout:
┌──────────────────────────────────┐
│ Title │
├──────────────────────────────────┤
│ │
│ ┌────────────────────────────┐ │
│ │ │ │
│ │ Visualization │ │
│ │ │ │
│ └────────────────────────────┘ │
│ │
│ ┌────────────────────────────┐ │
│ │ ▶ ⏸ ──●────── 1.0x ⟳ │ │
│ └────────────────────────────┘ │
│ │
│ ┌─────────┐ ┌──────────────┐ │
│ │ Control │ │ Explanatory │ │
│ │ Panel │ │ text that │ │
│ │ │ │ updates with │ │
│ │ │ │ context │ │
│ └─────────┘ └──────────────┘ │
└──────────────────────────────────┘
For scroll-driven explorables, use a two-column layout with sticky visualization:
┌──────────────┬───────────────────┐
│ │ │
│ Scrolling │ Sticky │
│ text with │ visualization │
│ controls │ that reacts to │
│ appearing │ scroll position │
│ per section │ │
│ │ │
└──────────────┴───────────────────┘
<style>When building an explorable explanation:
Build Progress:
- [ ] Step 1: Identify the core concept and what intuition the reader should leave with
- [ ] Step 2: Find the simplest possible interactive demonstration of that concept
- [ ] Step 3: Plan the progression — what controls appear and in what order
- [ ] Step 4: Build the base visualization with one interactive parameter
- [ ] Step 5: Layer in additional parameters one at a time
- [ ] Step 6: Add playback controls if animated
- [ ] Step 7: Write bridging text — short, contextual, appears alongside the relevant control
- [ ] Step 8: Test the pedagogical flow — does each step build on the last?
- [ ] Step 9: Polish — smooth transitions, meaningful defaults, edge case behavior
| Pattern | Why it fails |
|---|---|
| Long text intro before any interactivity | Reader disengages before reaching the interesting part |
| All controls visible from the start | Overwhelming; no guided learning path |
| Controls that don't visibly affect anything | Breaks the feedback loop; reader loses trust |
| Technical jargon before intuition | Terms without grounding don't stick |
| Clamping everything to "safe" ranges | Robs the reader of discovering edge cases |
| Tailwind arbitrary value classes (e.g., w-[37rem], text-[#4a9eff]) | Not available in single-file CDN/no-build setup; use standard utilities or fallback custom CSS |
| Static diagram where interaction would help | If the reader can't manipulate it, it's not an explorable |
data-ai
Extract captions and transcripts from YouTube videos for agent context. Tries manual subtitles, then auto-generated, then falls back to audio transcription via Whisper. Use when a user provides a YouTube URL and wants to understand, summarize, reference, or search video content.
tools
Official skill for XcodeBuildMCP. Use when doing iOS/macOS/watchOS/tvOS/visionOS work (build, test, run, debug, log, UI automation).
development
Write behavior-focused tests following Testing Trophy model with real dependencies, avoiding common anti-patterns like testing mocks and polluting production code. Use when writing new tests, reviewing test quality, or improving test coverage.
data-ai
Create professional Mermaid diagrams with proper styling and visual hierarchy. Use when creating flowcharts, sequence diagrams, state machines, class diagrams, or architecture visualizations.