Mock Player
How it works ↓Arrow keys and Enter work too.
Playback quality
Events 0
Select an event or request to inspect it.
How it works
A real HTML5 player wired for measurement. Every interaction becomes one canonical event that carries the player state,
content, ad, playback quality, and the viewer's identity and page context. Each event is then shown the way six vendors
would receive it: the library call a developer writes, the payload, and the exact HTTP request that library sends.
Nothing is sent to those vendors; the demo accounts below don't exist. Everything is also pushed to
window.dataLayer, so a GTM container you load fires for real.
Play plans
Long scripted sessions that drive the player through the same buttons a viewer uses. Run one and watch the console, or switch tabs mid-plan to follow a single vendor.
- First-time visitor, desktop · 19 steps · New visitor accepts consent, sits through a bumper and pre-roll, skips the intro, engages with chapters, CTA, like and share, changes quality, then picks a recommendation from the end card.
- Privacy-conscious visitor · 10 steps · Rejects all cookies. GA4 falls back to cookieless Consent Mode pings and Adobe, Segment, and Tealium are blocked. Later opts in from the panel, and full tracking resumes mid-session.
- Mobile commuter on a train · 13 steps · Phone in portrait on 4G. Rotates to fullscreen, drops to 3G then loses signal in a tunnel (stalls, ABR down-switches), recovers, gets backgrounded, and comes back.
- Living-room TV, live event · 12 steps · Smart TV with a remote. Joins the live stream during the pre-show slate, navigates with the D-pad, rewinds the DVR window, returns to live, rides out a technical-difficulties slate, and watches a stitched (SSAI) ad break.
- Paywall prospect from a paid social ad · 13 steps · Arrives from a Meta campaign (UTM), watches the 30 s free preview, hits the registration gate, dismisses it, comes back, signs in (identify links the anonymous ID), finishes, and clicks Subscribe.
- Ad ops audit: every ad type · 15 steps · Bumper, a two-ad pre-roll pod (one skipped, one clicked), companion banner, overlay banner, mid-roll, pause ad, post-roll, an empty VAST response, then the same break as SSAI with a filler slate.
- Controls tour: every button · 16 steps · Works through every control a viewer can touch: volume, mute, speed, theater, mini-player, playlist, autoplay, like and unlike, cast, audio description, chapters, overlay close, and the resume prompt (accept, then decline).
- Edge cases: where the viewer is · 11 steps · Same video from very different places: inside the origin data center, Seattle fiber, a Nebraska ranch on 56k dial-up, the same ranch on GEO satellite and Starlink, a misrouted viewer, Sydney with no CDN, and an offline gap. Compare startup time, ABR, and beacon latency in the Network tab.
- Chaos QA: everything breaks · 10 steps · A QA engineer walks the failure paths: bandwidth crash, rendition 404 failover, decode error with retry, DRM failure, then gates that fail: under-age viewer and a geo-blocked region.
Identity and context
The trainer keeps a first-party identity in local storage, the way cookies would: an anonymous ID, a GA client ID, an
Adobe ECID, an analytics session that times out after 30 minutes, a session counter, sign-in state and tier, an A/B
variant, consent, and campaign parameters. Every vendor format carries them where it expects them:
cid/uid/sid/sct for GA4, mid and eVars for Adobe Analytics,
an identityMap for Adobe Edge, anonymousId/userId/context for Segment, and
the UDO for Tealium. Signing in sends Segment's identify and GA4's login with a user_id.
Network path
Choose where the viewer is, how they connect, and which CDN edge serves them. Round-trip time is propagation over fiber (about 1.6× the straight-line distance at 200 km/ms) plus the last mile. Usable throughput is the link speed capped by what one TCP connection sustains at that RTT and packet loss, which is why a 25 Mbps GEO satellite link streams like DSL and 56k dial-up cannot hold even the 180p rendition. New connections pay DNS, TCP, and TLS before the first byte; every segment pays a round trip.
The same model times each analytics hit to that vendor's collection region. Far, lossy, or offline viewers produce slow, retried, queued, or dropped hits in the Network tab. Segment's library queues and retries, so its hits arrive late; GA4 and Adobe hits that fail are simply lost. That gap is a common reason vendor numbers disagree, and it is concentrated in rural and mobile audiences.
Consent
GTM always receives the push, because consent-aware tags decide what fires. GA4 switches to Consent Mode cookieless pings
(gcs=G100, no client ID, no user ID). Adobe, Segment, and Tealium are blocked until analytics consent is granted,
and the console marks those rows. Without advertising consent, ads are requested as non-personalized
(personalized: false, npa=1).
Vendors and demo accounts
- GTM · dataLayer.push with event name
mockplayer.<event> - GA4 · gtag.js, measurement ID G-JFWDEMO001, requests to /g/collect
- Adobe Analytics · AppMeasurement s.t / s.tl, report suite jfwdemoprod; eVars and events follow a sample solution design (eVar1 title, eVar2 id, eVar5 device, event1 start, event2–4 quartiles, event5 complete, event6 seconds watched, event10–15 ads)
- Adobe Media · Streaming Media via Edge Network (alloy), datastream 00000000-0000-0000-0000-00000demo01
- Segment · analytics.js Video Spec plus page / identify, write key DEMO_WRITE_KEY_NOT_REAL
- Tealium · utag.view / utag.link UDO and the Collect request to EventStream (jfw-demo/video)
Event reference
Every canonical event and what it becomes at each vendor (for a signed-in, fully consented viewer). A dash means that vendor has no equivalent call.
| Canonical | Category | GA4 | Adobe Analytics | Adobe Media | Segment | Tealium | When it fires |
|---|---|---|---|---|---|---|---|
session_start | session | — | — | media.sessionStart | — | video_session_start | Player loaded a piece of content and a viewing session began. |
session_end | session | video_session_end | — | media.sessionEnd | Video Playback Exited | video_session_end | Session closed (content changed, page hidden, or reset) with QoE totals. |
content_loaded | session | — | — | — | — | video_content_loaded | Metadata for the selected rendition is available (duration, size). |
playback_requested | playback | — | — | — | — | video_playback_requested | Viewer pressed play for the first time (start of startup timer). |
playback_started | playback | video_start | s.tl · event1 | media.play | Video Playback Started | video_playback_started | First frame of content rendered. Carries startup time. |
play | playback | video_resume | — | media.play | Video Playback Resumed | video_play | Playback resumed after a user pause. |
pause | playback | video_pause | — | media.pauseStart | Video Playback Paused | video_pause | Viewer paused playback. |
heartbeat | progress | — | s.tl · event6=10 | media.ping | Video Content Playing | video_heartbeat | Every 10 s of playing time (Adobe-style ping). |
milestone | progress | video_progress | s.tl · event2 | — | — | video_milestone | Crossed a percent milestone (10/25/50/75/90/95). |
content_completed | progress | video_complete | s.tl · event5 | media.sessionComplete | Video Playback Completed | video_content_completed | Reached the end of the content. |
seek_start | seek | — | — | media.pauseStart | Video Playback Seek Started | video_seek_start | Scrub or skip began. Carries the from-position. |
seek_complete | seek | video_seek | — | media.play | Video Playback Seek Completed | video_seek_complete | Playhead settled at the new position. |
buffer_start | buffer | video_buffer_start | s.tl · event20 | media.bufferStart | Video Playback Buffer Started | video_buffer_start | Playback stalled waiting for data (simulated or real network). |
buffer_end | buffer | video_buffer_end | — | media.play | Video Playback Buffer Completed | video_buffer_end | Enough data buffered; playback continues. Carries stall duration. |
bitrate_change | quality | video_quality_change | s.tl · event21 | media.bitrateChange | Video Quality Updated | video_bitrate_change | Rendition switched, by ABR or by the viewer. |
quality_mode_change | quality | — | — | — | — | video_quality_mode_change | Quality setting changed between Auto and a fixed rendition. |
network_change | quality | — | — | — | — | video_network_change | Network path changed: connection type, viewer location, or CDN routing. Carries RTT and throughput. |
ad_break_start | ad | video_ad_break_start | — | media.adBreakStart | Video Ad Break Started | video_ad_break_start | An ad pod began (pre, mid, or post-roll). |
ad_start | ad | video_ad_start | s.tl · event10 | media.adStart | Video Ad Started | video_ad_start | An individual ad in the pod started. |
ad_quartile | ad | video_ad_progress | — | — | Video Ad Playing | video_ad_quartile | Ad crossed 25 / 50 / 75 percent. |
ad_skip | ad | video_ad_skip | s.tl · event12 | media.adSkip | Video Ad Skipped | video_ad_skip | Viewer skipped a skippable ad. |
ad_click | ad | video_ad_click | s.tl · event13 | — | Video Ad Clicked | video_ad_click | Viewer clicked the ad (click-through). |
ad_complete | ad | video_ad_complete | s.tl · event11 | media.adComplete | Video Ad Completed | video_ad_complete | Ad played to the end. |
ad_break_end | ad | video_ad_break_end | — | media.adBreakComplete | Video Ad Break Completed | video_ad_break_end | Ad pod finished; content resumes. |
volume_change | ui | — | — | — | — | video_volume_change | Volume level changed (debounced). |
mute | ui | video_mute | — | media.statesUpdate | — | video_mute | Audio muted. |
unmute | ui | video_mute | — | media.statesUpdate | — | video_unmute | Audio unmuted. |
rate_change | ui | video_rate_change | — | — | — | video_rate_change | Playback speed changed. |
captions_on | ui | video_captions | — | media.statesUpdate | — | video_captions_on | Closed captions enabled. |
captions_off | ui | video_captions | — | media.statesUpdate | — | video_captions_off | Closed captions disabled. |
fullscreen_enter | ui | video_fullscreen | — | media.statesUpdate | — | video_fullscreen_enter | Entered fullscreen (native API or simulated in a device frame). |
fullscreen_exit | ui | — | — | media.statesUpdate | — | video_fullscreen_exit | Left fullscreen. |
pip_enter | ui | video_pip | — | media.statesUpdate | — | video_pip_enter | Entered picture-in-picture (native) or the floating mini-player. |
pip_exit | ui | — | — | media.statesUpdate | — | video_pip_exit | Left picture-in-picture or the mini-player. |
theater_on | ui | — | — | — | — | video_theater_on | Theater (wide) mode on. |
theater_off | ui | — | — | — | — | video_theater_off | Theater mode off. |
playlist_next | ui | — | — | — | — | video_playlist_next | Moved to the next item (button or autoplay). |
playlist_previous | ui | — | — | — | — | video_playlist_previous | Moved to the previous item. |
autoplay_toggle | ui | — | — | — | — | video_autoplay_toggle | Autoplay-next setting changed. |
go_live | seek | — | — | — | — | video_go_live | Live stream: jumped back to the live edge. |
form_factor_change | device | — | — | — | — | video_form_factor_change | Switched device type (desktop, mobile, tablet, TV, embed). |
orientation_change | device | — | — | — | — | video_orientation_change | Device rotated between portrait and landscape. |
visibility_change | device | — | — | — | — | video_visibility_change | Page hidden or shown (tab switch, app background). |
error | error | video_error | s.tl · event30 | media.error | Video Playback Interrupted | video_error | Playback, network, DRM, or ad error. Carries code and fatality. |
error_recovered | error | — | — | media.play | — | video_error_recovered | Player retried and recovered from a non-fatal error. |
overlay_ad_impression | ad | video_overlay_ad_view | — | — | Video Overlay Ad Viewed | video_overlay_ad_impression | Non-linear overlay banner shown over playing content. |
overlay_ad_click | ad | video_overlay_ad_click | s.tl · event14 | — | Video Overlay Ad Clicked | video_overlay_ad_click | Viewer clicked the overlay banner. |
overlay_ad_close | ad | — | — | — | Video Overlay Ad Closed | video_overlay_ad_close | Viewer closed the overlay banner. |
companion_impression | ad | video_companion_view | — | — | Companion Ad Viewed | video_companion_impression | Companion display banner shown beside the player during a linear ad. |
companion_click | ad | video_companion_click | s.tl · event15 | — | Companion Ad Clicked | video_companion_click | Viewer clicked the companion banner. |
pause_ad_impression | ad | video_pause_ad_view | — | — | Pause Ad Viewed | video_pause_ad_impression | Pause ad shown while content is paused. |
pause_ad_click | ad | video_pause_ad_click | — | — | Pause Ad Clicked | video_pause_ad_click | Viewer clicked the pause ad. |
slate_start | slate | video_slate_start | — | media.pauseStart | Video Slate Started | video_slate_start | A slate covers the content (pre-show, technical difficulties, ad filler). |
slate_end | slate | — | — | media.play | Video Slate Ended | video_slate_end | Slate removed; carries how long it was up. |
chapter_start | chapter | video_chapter_start | s.tl · event40 | media.chapterStart | Video Content Started | video_chapter_start | Playhead entered a chapter. |
chapter_complete | chapter | video_chapter_complete | — | media.chapterComplete | Video Content Completed | video_chapter_complete | Chapter played through to its end. |
chapter_skip | chapter | video_chapter_skip | — | media.chapterSkip | — | video_chapter_skip | Viewer left a chapter early by seeking or skipping. |
skip_intro | engagement | video_skip_intro | — | — | Video Intro Skipped | video_skip_intro | Viewer pressed Skip intro. |
cta_impression | engagement | video_cta_view | — | — | CTA Viewed | video_cta_impression | Timed call-to-action shown over the video. |
cta_click | engagement | video_cta_click | s.tl · event50 | — | CTA Clicked | video_cta_click | Viewer clicked the call-to-action. |
endcard_impression | engagement | video_endcard_view | — | — | Video Recommendations Viewed | video_endcard_impression | End card (watch next + subscribe) shown when content ends. |
endcard_click | engagement | video_endcard_click | s.tl · event51 | — | Video Recommendation Clicked | video_endcard_click | Viewer picked a recommendation on the end card. |
subscribe_click | engagement | video_subscribe_click | s.tl · event52 | — | Subscribe Clicked | video_subscribe_click | Viewer clicked Subscribe. |
resume_prompt | engagement | — | — | — | — | video_resume_prompt | Offered to resume from the saved position. |
resume_accept | engagement | video_resume_from_saved | — | media.play | Video Resume Accepted | video_resume_accept | Viewer resumed from the saved position. |
resume_decline | engagement | — | — | — | Video Resume Declined | video_resume_decline | Viewer chose to start over. |
share | engagement | share | s.tl · event53 | — | Video Shared | video_share | Viewer shared the video (copy link with timestamp). |
like | engagement | video_like | s.tl · event54 | — | Video Liked | video_like | Viewer liked the video. |
unlike | engagement | — | — | — | Video Unliked | video_unlike | Viewer removed their like. |
cast_start | device | video_cast_start | — | — | Video Cast Started | video_cast_start | Started casting to a TV (simulated Chromecast/AirPlay). |
cast_end | device | video_cast_end | — | — | Video Cast Ended | video_cast_end | Stopped casting; playback returns to this device. |
audio_track_change | ui | — | — | — | — | video_audio_track_change | Switched audio track (e.g. to audio description). |
gate_shown | gate | video_gate_view | s.tl · event61 | — | Gate Viewed | video_gate_shown | A gate blocked playback (consent, age, registration, geo). |
gate_passed | gate | video_gate_pass | s.tl · event62 | — | Gate Passed | video_gate_passed | Viewer cleared the gate. |
gate_failed | gate | video_gate_fail | — | — | Gate Failed | video_gate_failed | Viewer did not meet the gate (e.g. under age, outside region). |
gate_dismissed | gate | video_gate_dismiss | — | — | Gate Dismissed | video_gate_dismissed | Viewer closed the gate without clearing it. |
consent_update | identity | gtag('consent') | — | setConsent | Segment Consent Preference Updated | utag.gdpr.setConsentValue | Consent choices changed; vendors re-evaluate what they may send. |
sign_in | identity | login | s.tl · event60 | — | identify() | user_login | Viewer signed in; the anonymous ID is linked to a user ID (identify). |
sign_out | identity | — | — | — | Signed Out | video_sign_out | Viewer signed out. |
identity_reset | identity | first_visit | — | — | analytics.reset | video_identity_reset | Simulated a brand-new visitor (new anonymous ID, cookies cleared). |
campaign_arrival | identity | page_view | s.t() page view | — | page() | page_view | Simulated landing from a campaign link (UTM parameters). |
page_view | identity | page_view | s.t() page view | — | page() | page_view | Page loaded; the hit every vendor sends before any video event. |
Scripting it
The player exposes window.mockPlayer and a DOM event, so tests and demos can drive it:
window.addEventListener('mockplayer:event', (e) => console.log(e.detail.event, e.detail));
mockPlayer.runPlan('ad-ops'); // first-visit | privacy | commuter | living-room | paywall | ad-ops | controls-tour | edge-cases | chaos
mockPlayer.load('perseverance-landing');
mockPlayer.setConsent({ analytics: true, advertising: false, personalization: false });
mockPlayer.signIn();
mockPlayer.setSchedule({ bumper: true, ssai: true });
mockPlayer.setFlag('registrationGate', true);
mockPlayer.injectError('tech_slate');
mockPlayer.requests; // every simulated vendor request so far
mockPlayer.map('tealium', mockPlayer.events.at(-1)); Keyboard
Space/K play · J/L ±10 s · ←/→ ±5 s · ↑/↓ volume · M mute · C captions · F fullscreen · I mini-player · T theater · </> speed · 0–9 jump · Shift+N/P next/previous
Content
All clips are NASA public-domain media, streamed from images.nasa.gov in four renditions (180p to 1080p, about 0.3 to 5 Mbps), which is what the adaptive bitrate switches between. Captions are NASA's own, converted to WebVTT. Chapters, ads, CTAs, and gates are simulated on top. NASA does not endorse this tool.
- Artemis I Launches to the Moon (Recap) · HQ · 2022-11-16 · on demand
- NASA's Perseverance Rover Lands Successfully on Mars · JPL · 2021-02-18 · on demand
- SDO: Year 7 · GSFC · 2017-02-11 · on demand
- Earth Views from the International Space Station · HQ · 2019-04-30 · on demand
- James Webb Space Telescope: An Overview · GSFC · 2019-03-13 · on demand
- Hubble's 29th Anniversary · GSFC · 2019-04-24 · on demand
- Can You Spot Hubble: Short · GSFC · 2016-07-21 · used as ad creative
- Step Inside the International Space Station (Full Tour) · JSC · 2025-12-17 · simulated live stream
- Two Weeks in the Life of a Sunspot · GSFC · 2017-08-04 · on demand