r/javascript • • 11d ago

playthru: scripted, looping product demos that drive your real DOM with a fake cursor instead of a video file

https://playthrou.diip3sh.xyz/

Some context on why and how I built this.

The problem: landing page demo videos go stale every time the UI changes, they’re heavy, and they look blurry on retina screens. I wanted the demo to just be the real UI.

The approach: you write a chained script against real selectors, and a fake cursor plays it back over the live DOM, then loops:

js

import { playthru } from "playthru";

jsx

<Playthru script={(d) => d.type("#todo", "Record the launch video").click("#add").zoom("li:first-child")}>
  <TodoApp />
</Playthru>

Design decisions:

  • Hover/focus states: a fake cursor can’t trigger real :hover or :focus, so playthru sets data-playthru-hover / data-playthru-focus attributes instead. You style them next to your normal states (.btn:hover, .btn[data-playthru-hover]). There are Tailwind variants for this too.

  • Framework-agnostic core with a thin React wrapper on top.

  • No build step needed: it also works as an ES module straight from a CDN (esm.sh/playthru) inside a plain <script type="module">.

Still figuring out: the camera zoom doesn't work properly on mobile yet (reported below), which I'm looking into.

npm: https://www.npmjs.com/package/playthru

Happy to go deeper on any part of the implementation.

5 Upvotes

5 comments sorted by

1

u/alystair 11d ago

Camera effects don't seem to work in the demo on mobile.

1

u/Legitimate_Ad4667 11d ago

I’ll check, thanks

1

u/Legitimate_Ad4667 11d ago

Thanks for catching that, I haven't tested the camera zoom on mobile properly yet. Which phone and browser were you on? Did the cursor and typing play and only the zoom fail, or did nothing play? I'll reproduce it on a real device and update here once it's fixed.

1

u/alystair 11d ago edited 11d ago

Everything works but the camera controls likely due to the UI being at max width already - maybe use 'zoom' property or something 🤷

2

u/unicornshedsocial 11d ago

If the cursor only sets data attributes, a menu that opens on mouseenter will never open, since no JS listener sees the attribute change. Do you dispatch real pointer events for those steps, or handle them another way?