Install
This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.
Install straight from GitHub with WP-CLI:
wp plugin install https://github.com/hafez121/live-pulse/archive/refs/heads/main.zipReal-time viewer presence and instant emoji reactions for WordPress — built entirely on the native Interactivity API. No React, no build step, no third-party real-time service.
Live Pulse adds two small, live-feeling features to any post or page:
- 🟢 "X people are viewing this now" — a real-time viewer counter
- ⚡ An instant emoji reaction bar — tap a reaction, see it (and everyone else's) update immediately, no refresh
Why this is different
Most "who's viewing" or "reactions" plugins reach for a full JS framework or a third-party real-time service (Pusher, Firebase, a custom WebSocket server). Live Pulse doesn't use any of that.
It's built on the WordPress Interactivity API, introduced in WordPress 6.5 — a native reactivity runtime that ships with WordPress core itself. Combine that with polling against the standard REST API, and you get live-feeling UI with zero extra infrastructure.
Features
- Live viewer count, powered by self-expiring transients — no custom database table, no cron job
- Emoji reaction bar with one-reaction-per-visitor (tap again to remove, tap another to switch)
- Ships as a Gutenberg block, a
[live_pulse]shortcode, or auto-inserted before/after content - Fully configurable: post types, emoji set, poll interval, presence timeout, minimum viewers before the counter shows
- No visitor accounts — anonymous sessions use a random ID stored only in the browser
- Graceful no-JS fallback — the server-rendered count and totals are visible before JavaScript even loads
- Translation-ready, and cleans up after itself on uninstall if you opt in
How it works
Presence: each visitor's browser pings the server every few seconds (configurable) to say "I'm still here." The server keeps a short-lived list of active sessions per post in a transient and automatically forgets anyone who stops pinging — no cleanup job required.
Reactions: counts are stored in post meta as a simple emoji → count map. Each anonymous session can hold exactly one active reaction per post, remembered for a day, so switching or removing a reaction never inflates the numbers.
Installation
- Download the latest release ZIP (or clone this repo into
wp-content/plugins/live-pulse). - Activate Live Pulse from the Plugins screen.
- Go to Settings → Live Pulse to choose post types, emoji, and display options.
- Place it with the block, the
[live_pulse]shortcode, or leave auto-insert on.
Requires WordPress 6.5+ (for the Interactivity API) and PHP 7.4+.
File structure
live-pulse/
├── live-pulse.php Plugin bootstrap
├── uninstall.php Opt-in data cleanup
├── includes/
│ ├── class-live-pulse-settings.php Options + admin settings page
│ ├── class-live-pulse-presence.php Transient-based viewer tracking
│ ├── class-live-pulse-reactions.php Post-meta based reaction counts
│ ├── class-live-pulse-rest.php REST API routes
│ ├── class-live-pulse-display.php Shared render markup, shortcode, auto-insert
│ └── class-live-pulse-block.php Gutenberg block registration
├── blocks/live-pulse/
│ ├── block.json
│ ├── render.php Dynamic block render (reuses shared markup)
│ └── index.js Editor script (plain JS, no build step)
└── assets/
├── js/view.js Interactivity API store (presence + reactions)
└── css/ Frontend + admin styles
Why no build step?
Everything here is written to run as-is: no npm install, no webpack, no @wordpress/scripts. That's a deliberate choice — it keeps the codebase approachable to read end-to-end, and it means the Interactivity API's own runtime (already loaded by WordPress core for other blocks) is the only "framework" involved.
Known issues
- Reactions can intermittently fail to save on some shared hosts. During testing on a free host (InfinityFree), reaction clicks sometimes don't reach the REST endpoint at all, with no console error. Presence tracking (the viewer counter) works reliably in the same environment. Suspected causes: aggressive edge caching/proxying of
POSTrequests to/wp-json/on some free hosts, or a stale service worker/browser cache. Not yet reproduced on standard hosting — issue reports and PRs welcome. - Presence "leaves" are timeout-based, not instant. Closing a tab doesn't immediately decrement the viewer count — it expires naturally after the configured presence timeout (default 30s). This is a deliberate trade-off: an instant leave via
navigator.sendBeaconcan't reliably carry the REST nonce needed to authenticate the request.
Contributing
Issues and pull requests are welcome. A few ideas for a v2, if you'd like to help:
- Poll-based reactions on comments, not just posts
- A lightweight admin dashboard showing historical peak viewers
- An opt-in "who reacted" avatar stack for logged-in users
License
GPLv2 or later — see LICENSE.
Author
Built by Hafez Fandy — LinkedIn.