feat: mode-aware WPM ceiling — Sentence/Paragraph now go up to 3000 WPM (v1.1.1)

The WPM slider's maximum value now adapts to the active reading mode:
- Word mode: 1500 WPM ceiling (human single-word recognition limit)
- Sentence / Paragraph modes: 3000 WPM ceiling

Rationale: in chunk modes, "WPM" controls auto-advance timing across
larger display units. A 20-word sentence at 3000 WPM still gets ~400 ms
of display time — well within visual-recognition comfort and suitable
for skim-pass reading of already-familiar material.

Switching back to Word mode auto-clamps the current value down to 1500
to prevent accidentally-illegible word-mode playback.

The `+` keyboard shortcut now respects the mode-specific ceiling instead
of being hardcoded to 1500.
This commit is contained in:
2026-05-27 02:40:06 +01:00
parent 5e0ef5adf1
commit 669aabf5f2
2 changed files with 36 additions and 2 deletions
+17 -2
View File
@@ -250,7 +250,7 @@ textarea:focus {
</div>
<span class="meta" id="meta">Paste text below, drag a .txt file in, then press Play</span>
<span class="version">v1.1</span>
<span class="version">v1.1.1</span>
</div>
<div class="stage mode-word" id="stage">
@@ -513,11 +513,26 @@ textarea:focus {
}
// ─── Mode switching ────────────────────────────────────────────
// Mode-aware WPM ceiling. Word mode caps at 1500 — beyond that, the
// brain can't register single words faster than ~25ms per word. But
// Sentence and Paragraph modes display whole chunks at once; "WPM"
// there controls auto-advance timing, so values up to 3000 are
// genuinely useful for skim-reading (a 20-word sentence at 3000 WPM
// still gets ~400ms of display, which is comprehensible).
const WPM_MAX = { word: 1500, sentence: 3000, paragraph: 3000 };
function applyMode() {
stage.className = `stage mode-${mode}`;
[...modeToggle.querySelectorAll('button')].forEach(b => {
b.classList.toggle('active', b.dataset.mode === mode);
});
// Adjust the WPM ceiling for the current mode + clamp current value if it exceeds
const newMax = WPM_MAX[mode] || 1500;
wpmSlider.max = newMax;
if (parseInt(wpmSlider.value) > newMax) {
wpmSlider.value = newMax;
wpmVal.textContent = newMax;
}
}
function setMode(newMode) {
if (newMode === mode) return;
@@ -567,7 +582,7 @@ textarea:focus {
else if (k === '2') { setMode('sentence'); }
else if (k === '3') { setMode('paragraph'); }
else if (k === '+' || k === '=') {
wpmSlider.value = Math.min(1500, parseInt(wpmSlider.value) + 50);
wpmSlider.value = Math.min(parseInt(wpmSlider.max), parseInt(wpmSlider.value) + 50);
wpmVal.textContent = wpmSlider.value;
updateMeta();
save();