---
title: Play reading
---

<x-player-app />

# Read one thought at a time

[@link.play Start this reading](./play-reading.demo.md)

This example is deliberately longer than one viewport. It shows keyboard
ownership after activating the link above, stable Beat placement, free scroll
adoption, and quiet Player chrome without turning the document into a slide
deck.

---

## Prose rhythm

A short paragraph should land near the reading playhead without making the
page jump more than necessary.

This longer paragraph wraps across several lines so the Beat Guide has a real
content frame to follow. The guide should sit outside the readable lane, move
smoothly from the short paragraph, and leave enough space that it never looks
like punctuation attached to the sentence.

> A blockquote keeps its authored leading rule inside the guide. The guide
> follows the quote as one thought without covering its decoration.

---

## Lists and indentation

- First-level list markers stay visible.
- The guide aligns outside the marker lane.
- A longer list item wraps so its full height is represented.
  - Nested material keeps its indentation.
  - Moving between nested items should not jolt the viewport.
- The final item returns to the parent lane.

---

## A table with useful context

The first stop establishes the table. Later stops should highlight one body row
at a time while the header remains visible context.

| Phase  | Reader question    | Expected behavior           |
| ------ | ------------------ | --------------------------- |
| Enter  | Where am I?        | Table frame is active       |
| Step   | What changed?      | One body row is highlighted |
| Scroll | Can I look around? | Free scrolling stays native |
| Settle | Where do I resume? | Play adopts the visible row |

---

## Code as a readable Beat

An ordinary fence is one Beat. It should remain fully readable and reveal any
horizontal overflow inside the code surface rather than shifting the page.

```ts
export function readingPosition(beatTop: number, viewportHeight: number) {
  const playhead = viewportHeight * 0.42
  return Math.max(0, beatTop - playhead)
}
```

---

## A tall Beat

This Beat is intentionally tall. A tall Beat should align near the top instead
of centering its midpoint and hiding the opening lines. The reader can then
scroll naturally through it, and Play should wait until that free scrolling
settles before adopting a different Block. The surrounding spotlight relaxes
while the page is in motion so dimmed content never fights the reader. When the
scroll settles, Play updates once and does not snap the page back. This is also
where the compact controls need to stay out of the final lines on both desktop
and mobile. Enough copy follows to make the geometry meaningful without
turning the fixture into filler: Focus mode is a document reader first, so
selection, links, native scrolling, and normal responsive layout remain
available even while Play supplies cadence and orientation.

---

## Actions and continuation

The active Beat can offer an ordinary
[slide-deck continuation](./play-deck.demo.md). The action should be
reachable from the quiet chrome and preserve active Play settings when it stays
inside this PathMX site.

---

## Exit and restore

Escape stops Play and leaves this Block in view. Starting again from here
should select the nearest Beat instead of returning to the beginning.
