DEV Community

Cover image for Building a Hotjar-Class Analytics Platform in PHP: Heatmaps, Replays, Funnels, and Feedback
Jamie Folsom
Jamie Folsom

Posted on

Building a Hotjar-Class Analytics Platform in PHP: Heatmaps, Replays, Funnels, and Feedback

Most analytics tools answer what happened.

Behavior analytics answers why:

Where users clicked

How far they scrolled

Where they got stuck in forms

Where they dropped off in funnels

What they said (surveys/feedback) alongside what they did

I built Spyglass360, a Hotjar-style behavior analytics + CRO platform, using a stack a lot of people overlook:

PHP

MySQL

Vanilla JavaScript

Bootstrap

Square for subscriptions

This post is the engineering breakdown: the event model, storage strategy, and the “gotchas” you hit when you ship a real tracker.

The install contract: one snippet, stable URL

If install takes more than one snippet, you’ve already lost half your users.

Your tracker needs a stable public endpoint (don’t leak internal file paths):

(function(){
var s = document.createElement("script");
s.src = "https://spyglass360.com/track.js?api=YOUR_API_KEY";
s.async = true;
document.head.appendChild(s);
})();

What is this?

Rule: /track.js should remain stable even if you refactor your internal /assets/... paths later.

Event design: collect the minimum data that enables the most features

A lot of tracking systems fail because they capture too much (privacy risk, storage cost), or too little (can’t compute anything useful).

A workable base event model:

pageview (url, referrer, viewport)

click (normalized x/y + element hint)

scroll (depth percent)

form_focus, form_blur, form_submit (never raw values)

custom_event (for funnels/goals)

Example payload:

{
"apiKey": "…",
"siteId": 123,
"visitorId": "v_abc",
"sessionId": "s_xyz",
"type": "click",
"url": "https://example.com/pricing",
"ts": 1710000000000,
"viewport": { "w": 1440, "h": 900 },
"event": { "x": 0.62, "y": 0.41, "selector": "button#start" }
}

Normalization matters: store coordinates as percentages so heatmaps work across screen sizes.

Storage strategy: raw events + aggregates

PHP can handle ingestion just fine. The real problem is query shape.

The common pattern:

Write all events to an append-only raw store

Maintain aggregate tables for dashboard queries

Raw table (append-only)

fast inserts

flexible schema via JSON payload

used for replay + deep analysis

Aggregate tables (fast reads)

daily/site stats

funnels summary

form dropoff stats

heatmap bins per page

This lets you keep MySQL without instantly needing ClickHouse/BigQuery.

Heatmaps: bins + rendering

Heatmaps are basically “count points and draw a gradient”.

Implementation approach:

Normalize x,y

Convert to bins (e.g., 100×100 grid)

Increment bin counters

Render bins as an overlay in the dashboard

Why bins:

you avoid storing every pixel as its own row

you can pre-aggregate quickly

rendering becomes predictable

Session replay: not video, a timeline

Replay looks like video, but it’s really:

an initial snapshot

a stream of events

a player that reconstructs the session

An MVP replay can start with:

click trail + scroll positions + page navigation timeline

Then evolve into:

DOM snapshots

mutations

masking rules

Big rule:

Mask and sanitize by default. Replay must never leak passwords, credit cards, or sensitive fields.

Funnels: pay attention to correlation, not just counts

Funnels work when you can answer:

“Did this same session reach step 1 → 2 → 3?”

“How long between steps?”

“Where is the dropoff?”

Implementation:

define funnel steps by URL patterns and/or event names

compute step completion per session

aggregate dropoffs

Funnels are one of the highest paid-value features because they directly tie to revenue.

Form analytics: capture friction without capturing values

Form tools get creepy fast. Don’t collect raw input.

Instead track:

field focus/blur

time-in-field

submit attempt

validation errors (optional)

Output:

“field X is where 60% abandon”

“average time spent here is 12s”

“users rage-click submit then bounce”

Feedback widgets + surveys: the “why” layer

Behavior tells you what. Feedback tells you why.

What matters is targeting:

show surveys based on pages, events, or user segments

connect responses to session context (without exposing identity)

Once you can connect:

“users rage click here”
with

“the pricing is confusing”
you get actionable insight.

Experiments (A/B testing): integrated beats bolted-on

Once you have:

visitors/sessions

conversion goals (events)

segmentation

…experiments become much easier:

assign variant consistently per visitor

measure goal completion

break results down by segment

The win isn’t “A/B testing exists”, it’s:

“Testing is integrated with behavior + funnels + feedback.”

Billing gotchas: Square subscription plan variation IDs

If you use Square subscriptions, here’s the sharp edge:

Subscription enrollment requires the SUBSCRIPTION_PLAN_VARIATION ID, not the plan ID.

If you pass the wrong one, you’ll see errors like:

Object does not have type SUBSCRIPTION_PLAN_VARIATION

Fix:

create the plan AND variation in your admin tooling

store both IDs

always enroll by variation ID

Launch gotcha: never grant access before payment success

The classic SaaS bug:

user picks paid plan

you create account and log them in immediately

payment never completes

they still get access

Fix with a proper state machine:

pending_payment

active

failed/canceled

Gate the dashboard/API based on active.

What I’d recommend if you build something like this

Start with funnels + form analytics (they sell)

Heatmaps are easy value

Replay is hardest (build in layers)

Lock down privacy early

Keep /track.js stable forever

Add “install verification” UX so users know it’s working

Wrapping up

Building behavior analytics is less about “writing a tracker” and more about:

event design

storage strategy

privacy discipline

and turning data into actionable UX

If you’re building something similar, I’d love to hear:

what feature is hardest for you?

what you wish Hotjar-style tools did better?

(If you want to try Spyglass360, it’s at spyglass360.com.)

Top comments (0)