⏳ This skill is pending AI review.

Scores will appear once the review pipeline completes.

version unknown

speed

@seanzor⭐ 18 stars

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.

—/10

// RATINGS

⭐GitHub Stars
⭐⭐ 18 on GitHubGitHub ↗

Growing

🟢ProSkills Score
—
📍

Not yet listed on ClawHub or SkillsMP

// README

claude-speed-reader

Speed read Claude's responses at 600+ WPM using RSVP with Spritz-style ORP highlighting.

Speed Reader Demo

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

KeyAction
SpacePlay / Pause
← →Adjust speed (±50 WPM)
RRestart
VPaste 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 open command)

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>&larr;</kbd><kbd>&rarr;</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

// REPO STATS

18 stars