Hero Shapes Analytics Tracking
Complete analytics tracking system for user interactions within the Hero Shapes physics section. All events are pushed to window.dataLayer (for Google Tag Manager) or fall back to gtag() if GTM is not present.
Overview
The tracking system monitors user interactions with the physics shapes, including drags, clicks, and time spent interacting. All tracking is session-based and designed to be lightweight and non-intrusive to the physics engine performance.
Implementation
The analytics system is integrated into hero-shapes.js and uses a modular approach:
- State Management: Lightweight in-memory state tracking
- Event Helper: Single
trackEvent()function that handles both GTM and gtag - Event Hooks: Integrated into existing Matter.js event listeners
- No Performance Impact: Tracking runs asynchronously and doesn't block physics calculations
Tracked Events
1. interaction_started
Trigger: Fires the moment the user performs their first drag in the session.
Fires Once: Yes, only once per session.
Event Data:
{
event: "interaction_started",
timestamp: 1234567890123,
shape_label: "news" // or null if drag didn't start on a shape
}
When It Fires:
- First time a user starts dragging any shape
- Before any other interaction events
- Starts the interaction timer
2. first_shape_dragged
Trigger: Fires only the first time a user drags a shape.
Fires Once: Yes, only once per session.
Event Data:
{
event: "first_shape_dragged",
shape_label: "news",
shape_index: 0,
timestamp: 1234567890123
}
When It Fires:
- First drag of any shape in the session
- Only if the drag started on an actual shape (not logo or walls)
3. shape_dragged
Trigger: Fires every time a drag begins.
Fires Once: No, fires on every drag.
Event Data:
{
event: "shape_dragged",
shape_label: "founders",
total_drags: 5,
timestamp: 1234567890123
}
Properties:
total_drags: Increments with each drag (session counter)shape_label: Label of the shape being dragged
When It Fires:
- Every time a user starts dragging a shape
- Increments the session drag counter
- Adds shape to unique shapes set
4. shape_clicked
Trigger: When a shape is clicked (not dragged).
Fires Once: No, fires on every click.
Event Data:
{
event: "shape_clicked",
shape_label: "work",
timestamp: 1234567890123
}
Click Detection:
- Uses existing 5px distance threshold
- If mouse movement < 5px between mousedown and mouseup = click
- If mouse movement >= 5px = drag (not tracked as click)
When It Fires:
- User clicks a shape without dragging
- Only fires for actual shapes (not logo or walls)
5. time_spent_interacting
Trigger: Fires when user becomes inactive or page unloads.
Fires Once: Per interaction session.
Event Data:
{
event: "time_spent_interacting",
interaction_duration_ms: 15420,
total_drags: 8,
shapes_interacted_count: 3,
timestamp: 1234567890123
}
Properties:
interaction_duration_ms: Time frominteraction_startedto inactivity/unloadtotal_drags: Total number of drags in the sessionshapes_interacted_count: Number of unique shapes interacted with
When It Fires:
- After 3 seconds of inactivity (no drags)
- On page unload (
beforeunloadevent) - Timer starts when
interaction_startedfires
Inactivity Detection:
- 3-second timeout after last drag ends
- Timer resets on each new drag
- Only fires if an interaction was started
6. shapes_interacted_count
Logic: Maintains a Set of unique shape labels the user has interacted with.
Included In: time_spent_interacting event (as shapes_interacted_count property).
How It Works:
- Each shape label is added to a Set when dragged
- Set automatically handles uniqueness
- Count is included in
time_spent_interactingevent
7. reset_button_clicked
Trigger: Fires when the reset button is clicked.
Fires Once: No, fires every time reset is clicked.
Event Data:
{
event: "reset_button_clicked",
timestamp: 1234567890123,
total_drags: 12,
interaction_duration_ms: 28450
}
Properties:
total_drags: Total drags so far in the sessioninteraction_duration_ms: Time sinceinteraction_started(if started)
When It Fires:
- User clicks the reset button (
#button-on-canvas) - Fires before shapes are reset
- Includes current session stats
Technical Implementation
Event Helper Function
function trackEvent(eventName, eventData = {}) {
// Adds timestamp if not provided
if (!eventData.timestamp) {
eventData.timestamp = Date.now();
}
// Push to dataLayer for GTM
if (window.dataLayer && Array.isArray(window.dataLayer)) {
window.dataLayer.push({
event: eventName,
...eventData
});
}
// Fallback to gtag if available
else if (typeof gtag === 'function') {
gtag('event', eventName, eventData);
}
}
State Management
All tracking state is stored in window.stackingShapes.analytics:
{
hasStarted: false, // Has interaction_started fired?
hasDraggedFirstShape: false, // Has first_shape_dragged fired?
interactionStartTime: null, // When did interaction start?
totalDrags: 0, // Session drag counter
shapesSet: new Set(), // Unique shapes interacted with
inactivityTimer: null, // Inactivity timeout reference
INACTIVITY_TIMEOUT: 3000 // 3 seconds
}
Event Hooks
Tracking is integrated into existing Matter.js event listeners:
startdragevent -interaction_started,first_shape_dragged,shape_draggedenddragevent - Inactivity timer checkmouseupevent -shape_clicked(if distance < 5px)beforeunloadevent -time_spent_interacting- Reset button click -
reset_button_clicked
Data Layer Format
All events are pushed to window.dataLayer in this format:
window.dataLayer.push({
event: "event_name",
property1: "value1",
property2: "value2",
timestamp: 1234567890123
});
GTM Configuration
In Google Tag Manager, you can create triggers based on these events:
- Trigger Type: Custom Event
- Event Name:
interaction_started,shape_dragged, etc. - Variables: Access event properties like
shape_label,total_drags, etc.
Example GTM Tag Configuration
Tag: Google Analytics: GA4 Event
- Event Name:
hero_shapes_interaction - Event Parameters:
shape_label:{{shape_label}}total_drags:{{total_drags}}interaction_duration_ms:{{interaction_duration_ms}}
Trigger: Custom Event
- Event Name:
shape_dragged
Session Behavior
- Session Scope: All tracking is per-page-session
- State Persistence: State resets on page reload
- No Duplicates:
interaction_startedandfirst_shape_draggedfire only once - Counter Reset:
total_dragsresets on page reload
Performance Considerations
- Non-Blocking: All tracking is asynchronous
- Lightweight: Minimal state management overhead
- No Physics Impact: Tracking doesn't interfere with Matter.js calculations
- Efficient: Uses Set for unique shape tracking (O(1) lookups)
Testing
To test the tracking system:
- Open browser console
- Check
window.dataLayerarray for pushed events - Or use GTM Preview mode to see events in real-time
- Interact with shapes to trigger events
Console Testing
// Check if dataLayer exists
console.log(window.dataLayer);
// Filter for hero shapes events
window.dataLayer.filter(e => e.event && e.event.includes('shape') || e.event === 'interaction_started');
Event Flow Example
- User drags shape -
interaction_startedfires - Same drag -
first_shape_draggedfires - Same drag -
shape_draggedfires (total_drags: 1) - User drags another shape -
shape_draggedfires (total_drags: 2) - User stops for 3 seconds -
time_spent_interactingfires - User clicks reset -
reset_button_clickedfires