⏳ This skill is pending AI review.
Scores will appear once the review pipeline completes.
speed
Launch RSVP speed reader for text
Choose how to use this skill
You do not need every option. Choose the path your AI client supports. The stable page stays the same; versioned files are immutable.
1. Native installer
This listing has no registered native installer command. Use the complete package or source fallback below, depending on what your client supports.
Do not guess an installer command or replace an existing version without reviewing the diff.
2. Complete package recommended
Download the ZIP when available. It includes SKILL.md plus the references, security notes and version metadata.
No complete ProSkills package is published for this listing yet.3. Prompt-only
Copy the prompt above when the agent can read the stable page or when you want to adopt the workflow without installing a skill.
Need only the instruction file?
Download SKILL.md only if your client requires a single file. The complete ZIP is safer for a full installation because it preserves the references and release context.
No path installs or executes anything by itself. Your agent still needs access to the project files. Before updating, compare the installed version and review the diff.
// RATINGS
// README
claude-speed-reader
Speed read Claude's responses at 600+ WPM using RSVP with Spritz-style ORP highlighting.

What is this?
A Claude Code skill that lets you speed-read any response. Uses Rapid Serial Visual Presentation (RSVP) — displaying one word at a time with the Optimal Recognition Point (ORP) highlighted in red. Your eyes stay fixed while your brain processes text at 2-3x normal reading speed.
Install
# Clone to your Claude skills directory
git clone https://github.com/SeanZoR/claude-speed-reader.git ~/.claude/skills/speed
Or manually copy the .claude/ folder contents to your ~/.claude/ directory.
Usage
In Claude Code, after any response:
/speed
That's it. Opens a minimal dark reader with the last response loaded.
Custom text:
/speed "Your text here"
Controls
| Key | Action |
|---|---|
Space | Play / Pause |
← → | Adjust speed (±50 WPM) |
R | Restart |
V | Paste new text |
Click anywhere to toggle play/pause. Drag & drop text files supported.
How ORP Works
The red highlighted letter is the Optimal Recognition Point — positioned ~1/3 into each word where your brain naturally focuses. By keeping this point fixed on screen, you eliminate eye movement (saccades) that consume 80% of reading time.
th[e] quick br[o]wn fox ju[m]ps
↑ ↑ ↑
ORP ORP ORP
Customization
Edit ~/.claude/skills/speed/data/reader.html to customize:
- Default WPM (currently 600)
- Colors and fonts
- Timing multipliers for punctuation
Requirements
- Claude Code
- macOS (uses
opencommand)
License
MIT
Built for humans who want to read Claude's novels faster.
// HOW IT'S BUILT
// FILES
3 files shipped with this skill. Inline text is the catalog copy. The upstream link is the source repository.
.claude/skills/speed/SKILL.md1.0 KB68c17961abeaupstream ↗
--- name: speed description: Launch RSVP speed reader for text trigger: command tools: Write, Bash, Read --- # Speed Reader Launch the RSVP speed reader to display text one word at a time with Spritz-style ORP (Optimal Recognition Point) highlighting. ## Instructions 1. **Get the text:** - If `$ARGUMENTS` is provided, use that text - Otherwise, extract the main content from your **previous response** in this conversation 2. **Prepare the content:** - Strip markdown formatting (headers, bold, links, code blocks) - Keep clean, readable prose - Escape quotes and backslashes for JavaScript 3. **Write and launch:** - Read `~/.claude/skills/speed/data/reader.html` - Replace `<!-- CONTENT_PLACEHOLDER -->` with: ```html <script>window.SPEED_READER_CONTENT = "your escaped text";</script> <!-- CONTENT_PLACEHOLDER --> ``` - Run: `open ~/.claude/skills/speed/data/reader.html` 4. **Confirm:** Tell the user it's opening. Mention `Space` to play/pause. ## Arguments $ARGUMENTS.claude/skills/speed/data/reader.html14 KBd9a97e59da2fupstream ↗
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Speed Reader</title> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet"> <style> * { box-sizing: border-box; margin: 0; padding: 0; } :root { --bg: #0a0a0f; --surface: #12121a; --border: #1e1e2a; --text: #e4e4e7; --muted: #52525b; --accent: #ef4444; --accent-dim: rgba(239, 68, 68, 0.15); } body { font-family: 'JetBrains Mono', monospace; background: var(--bg); color: var(--text); min-height: 100vh; display: flex; flex-direction: column; overflow: hidden; } /* Progress bar - thin line at top */ .progress { position: fixed; top: 0; left: 0; height: 2px; background: var(--accent); width: 0%; transition: width 0.1s linear; z-index: 100; } /* Main reading area */ .reader { flex: 1; display: flex; align-items: center; justify-content: center; cursor: pointer; user-select: none; } .word-container { position: relative; width: 100%; display: flex; justify-content: center; } /* Focus line indicator */ .focus-line { position: fixed; top: 30%; bottom: 40%; left: 50%; transform: translateX(-50%); width: 2px; background: linear-gradient(to bottom, transparent, var(--accent), transparent); opacity: 0.2; pointer-events: none; } .word { font-size: clamp(2.5rem, 8vw, 5rem); font-weight: 500; letter-spacing: 0.02em; white-space: nowrap; display: flex; align-items: baseline; } .word-before { color: var(--text); width: 45vw; text-align: right; } .word-after { color: var(--text); width: 45vw; text-align: left; } .word-orp { color: var(--accent); position: relative; text-align: center; min-width: 0.6em; } .word-orp::after { content: ''; position: absolute; bottom: -4px; left: 0; right: 0; height: 2px; background: var(--accent); opacity: 0.5; } /* Paused state */ .paused .word { opacity: 0.5; } .paused .word-orp { color: var(--muted); } .paused .word-orp::after { background: var(--muted); } /* Controls - appear on hover/pause */ .controls { position: fixed; bottom: 0; left: 0; right: 0; padding: 1.5rem 2rem; background: linear-gradient(to top, var(--bg), transparent); opacity: 0; transform: translateY(10px); transition: all 0.3s ease; pointer-events: none; } body:hover .controls, .paused .controls, .idle .controls { opacity: 1; transform: translateY(0); pointer-events: auto; } .controls-inner { max-width: 600px; margin: 0 auto; display: flex; flex-direction: column; gap: 1rem; } .speed-row { display: flex; align-items: center; gap: 1rem; } .wpm { font-size: 0.875rem; color: var(--muted); min-width: 80px; } .wpm span { color: var(--accent); font-weight: 600; } input[type="range"] { flex: 1; height: 4px; -webkit-appearance: none; background: var(--border); border-radius: 2px; outline: none; } input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 14px; height: 14px; background: var(--accent); border-radius: 50%; cursor: pointer; transition: transform 0.15s; } input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.3); } .btn-row { display: flex; gap: 0.5rem; } button { flex: 1; padding: 0.625rem 1rem; font-family: inherit; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; border: 1px solid var(--border); border-radius: 6px; background: var(--surface); color: var(--muted); cursor: pointer; transition: all 0.15s; } button:hover { border-color: var(--accent); color: var(--text); } button.primary { background: var(--accent); border-color: var(--accent); color: white; } /* Status */ .status { position: fixed; top: 1rem; left: 50%; transform: translateX(-50%); font-size: 0.75rem; color: var(--muted); opacity: 0; transition: opacity 0.3s; } body:hover .status, .paused .status, .idle .status { opacity: 1; } /* Keyboard hints */ .hints { position: fixed; bottom: 1rem; right: 1rem; font-size: 0.625rem; color: var(--muted); opacity: 0; transition: opacity 0.3s; } body:hover .hints { opacity: 0.5; } kbd { display: inline-block; padding: 0.15rem 0.4rem; background: var(--surface); border: 1px solid var(--border); border-radius: 3px; margin: 0 0.15rem; } /* Input modal */ .modal { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.8); display: flex; align-items: center; justify-content: center; padding: 2rem; opacity: 0; pointer-events: none; transition: opacity 0.2s; } .modal.open { opacity: 1; pointer-events: auto; } .modal-content { width: 100%; max-width: 500px; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 1.5rem; } .modal h2 { font-size: 0.875rem; font-weight: 500; margin-bottom: 1rem; color: var(--muted); } textarea { width: 100%; min-height: 150px; padding: 1rem; font-family: inherit; font-size: 0.875rem; background: var(--bg); border: 1px solid var(--border); border-radius: 8px; color: var(--text); resize: vertical; outline: none; } textarea:focus { border-color: var(--accent); } .modal-buttons { display: flex; gap: 0.5rem; margin-top: 1rem; } /* Drop zone overlay */ .drop-overlay { position: fixed; inset: 0; background: rgba(10, 10, 15, 0.95); display: flex; align-items: center; justify-content: center; font-size: 1.25rem; color: var(--accent); opacity: 0; pointer-events: none; transition: opacity 0.2s; } .drop-overlay.active { opacity: 1; pointer-events: auto; } </style> </head> <body class="idle"> <div class="progress" id="progress"></div> <div class="status" id="status">Press Space to start</div> <div class="reader" id="reader"> <div class="word-container"> <div class="focus-line"></div> <div class="word" id="word"> <span class="word-before"></span><span class="word-orp"></span><span class="word-after"></span> </div> </div> </div> <div class="controls"> <div class="controls-inner"> <div class="speed-row"> <div class="wpm"><span id="wpmDisplay">600</span> wpm</div> <input type="range" id="speedSlider" min="100" max="1500" value="600" step="50"> </div> <div class="btn-row"> <button class="primary" id="playBtn">Play</button> <button id="restartBtn">Restart</button> <button id="pasteBtn">Paste</button> </div> </div> </div> <div class="hints"> <kbd>Space</kbd> play <kbd>←</kbd><kbd>→</kbd> speed <kbd>R</kbd> restart <kbd>V</kbd> paste </div> <div class="modal" id="modal"> <div class="modal-content"> <h2>Paste or drop text</h2> <textarea id="textInput" placeholder="Paste your text here..."></textarea> <div class="modal-buttons"> <button id="loadBtn" class="primary">Load</button> <button id="cancelBtn">Cancel</button> </div> </div> </div> <div class="drop-overlay" id="dropOverlay">Drop text file here</div> <script>window.SPEED_READER_CONTENT = "Memento is a Claude Code command that extracts session memories into CLAUDE.md files. Core idea: After a coding session, /memento analyzes your conversation to find learnings that should persist - things like project conventions, user preferences, or patterns discovered during debugging. Key philosophy: Suggestions must be actionable - they should change Claude's future behavior. Vague observations get filtered out. Structure: .claude/commands/memento.md is the main memory extraction command. .claude/commands/brag.md is for generating social content ideas. docs/ and examples/ contain documentation and example scenarios. Memory types: Project Memory goes in ./CLAUDE.md for codebase-specific conventions. User Memory goes in ~/.claude/CLAUDE.md for cross-project preferences. Planned components include Memento MCP, a TypeScript server to read session history, and Memento Sync for Git-based backup with smart CLAUDE.md merging. The command is designed to be symlinked to ~/.claude/commands/ for global use across all projects.";</script> <!-- CONTENT_PLACEHOLDER --> <script> const $ = id => document.getElementById(id); let words = [], index = 0, wpm = 600, playing = false, timer = null; // ORP: ~1/3 into word const orp = w => w.length > 13 ? 4 : [0,0,1,1,1,1,2,2,2,2,3,3,3,3][w.length]; // Timing: extra pause for punctuation const timing = (w, base) => { let t = base; if (/[.!?]$/.test(w)) t *= 2.2; else if (/[,;:]$/.test(w)) t *= 1.5; if (w.length > 10) t *= 1.1; return t; }; const display = w => { if (!w) return; const i = orp(w); $('word').innerHTML = `<span class="word-before">${esc(w.slice(0,i))}</span><span class="word-orp">${esc(w[i]||'')}</span><span class="word-after">${esc(w.slice(i+1))}</span>`; }; const esc = s => { const d = document.createElement('div'); d.textContent = s; return d.innerHTML; }; const updateProgress = () => { const pct = words.length ? (index / words.length) * 100 : 0; $('progress').style.width = pct + '%'; const remaining = Math.ceil((words.length - index) / wpm * 60); $('status').textContent = playing ? `${Math.floor(remaining/60)}:${(remaining%60).toString().padStart(2,'0')} remaining` : words.length ? `${words.length} words` : 'Paste text to begin'; }; const next = () => { if (!playing || index >= words.length) { if (index >= words.length && words.length) stop(); return; } display(words[index]); updateProgress(); index++; timer = setTimeout(next, timing(words[index-1], 60000/wpm)); }; const play = () => { if (!words.length) { openModal(); return; } playing = true; document.body.classList.remove('paused', 'idle'); $('playBtn').textContent = 'Pause'; next(); }; const pause = () => { playing = false; clearTimeout(timer); document.body.classList.add('paused'); $('playBtn').textContent = 'Play'; updateProgress(); }; const stop = () => { pause(); $('status').textContent = 'Done'; }; const toggle = () => playing ? pause() : play(); const restart = () => { pause(); index = 0; if (words[0]) display(words[0]); updateProgress(); document.body.classList.add('idle'); }; const load = text => { pause(); words = text.replace(/\s+/g, ' ').trim().split(' ').filter(w => w); index = 0; if (words.length) { display(words[0]); document.body.classList.add('idle'); } updateProgress(); closeModal(); }; const openModal = () => { $('modal').classList.add('open'); $('textInput').focus(); }; const closeModal = () => { $('modal').classList.remove('open'); }; // Events $('reader').onclick = toggle; $('playBtn').onclick = toggle; $('restartBtn').onclick = restart; $('pasteBtn').onclick = openModal; $('loadBtn').onclick = () => load($('textInput').value); $('cancelBtn').onclick = closeModal; $('speedSlider').oninput = e => { wpm = +e.target.value; $('wpmDisplay').textContent = wpm; localStorage.setItem('speed-wpm', wpm); }; document.onkeydown = e => { if ($('modal').classList.contains('open')) { if (e.key === 'Escape') closeModal(); return; } if (e.target.tagName === 'TEXTAREA') return; switch(e.code) { case 'Space': e.preventDefault(); toggle(); break; case 'ArrowLeft': e.preventDefault(); wpm = Math.max(100, wpm-50); $('speedSlider').value = wpm; $('wpmDisplay').textContent = wpm; break; case 'ArrowRight': e.preventDefault(); wpm = Math.min(1500, wpm+50); $('speedSlider').value = wpm; $('wpmDisplay').textContent = wpm; break; case 'KeyR': e.preventDefault(); restart(); break; case 'KeyV': e.preventDefault(); openModal(); break; } }; // Drag & drop document.ondragover = e => { e.preventDefault(); $('dropOverlay').classList.add('active'); }; document.ondragleave = e => { if (e.relatedTarget === null) $('dropOverlay').classList.remove('active'); }; document.ondrop = e => { e.preventDefault(); $('dropOverlay').classList.remove('active'); const file = e.dataTransfer.files[0]; if (file) { const reader = new FileReader(); reader.onload = ev => load(ev.target.result); reader.readAsText(file); } }; // Init const saved = localStorage.getItem('speed-wpm'); if (saved) { wpm = +saved; $('speedSlider').value = wpm; $('wpmDisplay').textContent = wpm; } if (typeof window.SPEED_READER_CONTENT !== 'undefined' && window.SPEED_READER_CONTENT.trim()) { load(window.SPEED_READER_CONTENT); } else { updateProgress(); } </script> </body> </html>README.mdupstream ↗
Contents are not inlined in the catalog. Open the upstream file.
Plain-text copy, no login: /skill-bundles/seanzor-claude-speed-reader-claude-skills-speed.md