Mock Player

Video analytics trainer · 8 NASA clips · 82 events · 6 vendors · 9 play plans How it works ↓

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.

    1. 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.
    2. 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.
    3. 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.
    4. 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.
    5. 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.
    6. 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.
    7. 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).
    8. 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.
    9. 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.

    CanonicalCategoryGA4Adobe AnalyticsAdobe MediaSegmentTealiumWhen it fires
    session_startsession——media.sessionStart—video_session_startPlayer loaded a piece of content and a viewing session began.
    session_endsessionvideo_session_end—media.sessionEndVideo Playback Exitedvideo_session_endSession closed (content changed, page hidden, or reset) with QoE totals.
    content_loadedsession————video_content_loadedMetadata for the selected rendition is available (duration, size).
    playback_requestedplayback————video_playback_requestedViewer pressed play for the first time (start of startup timer).
    playback_startedplaybackvideo_starts.tl · event1media.playVideo Playback Startedvideo_playback_startedFirst frame of content rendered. Carries startup time.
    playplaybackvideo_resume—media.playVideo Playback Resumedvideo_playPlayback resumed after a user pause.
    pauseplaybackvideo_pause—media.pauseStartVideo Playback Pausedvideo_pauseViewer paused playback.
    heartbeatprogress—s.tl · event6=10media.pingVideo Content Playingvideo_heartbeatEvery 10 s of playing time (Adobe-style ping).
    milestoneprogressvideo_progresss.tl · event2——video_milestoneCrossed a percent milestone (10/25/50/75/90/95).
    content_completedprogressvideo_completes.tl · event5media.sessionCompleteVideo Playback Completedvideo_content_completedReached the end of the content.
    seek_startseek——media.pauseStartVideo Playback Seek Startedvideo_seek_startScrub or skip began. Carries the from-position.
    seek_completeseekvideo_seek—media.playVideo Playback Seek Completedvideo_seek_completePlayhead settled at the new position.
    buffer_startbuffervideo_buffer_starts.tl · event20media.bufferStartVideo Playback Buffer Startedvideo_buffer_startPlayback stalled waiting for data (simulated or real network).
    buffer_endbuffervideo_buffer_end—media.playVideo Playback Buffer Completedvideo_buffer_endEnough data buffered; playback continues. Carries stall duration.
    bitrate_changequalityvideo_quality_changes.tl · event21media.bitrateChangeVideo Quality Updatedvideo_bitrate_changeRendition switched, by ABR or by the viewer.
    quality_mode_changequality————video_quality_mode_changeQuality setting changed between Auto and a fixed rendition.
    network_changequality————video_network_changeNetwork path changed: connection type, viewer location, or CDN routing. Carries RTT and throughput.
    ad_break_startadvideo_ad_break_start—media.adBreakStartVideo Ad Break Startedvideo_ad_break_startAn ad pod began (pre, mid, or post-roll).
    ad_startadvideo_ad_starts.tl · event10media.adStartVideo Ad Startedvideo_ad_startAn individual ad in the pod started.
    ad_quartileadvideo_ad_progress——Video Ad Playingvideo_ad_quartileAd crossed 25 / 50 / 75 percent.
    ad_skipadvideo_ad_skips.tl · event12media.adSkipVideo Ad Skippedvideo_ad_skipViewer skipped a skippable ad.
    ad_clickadvideo_ad_clicks.tl · event13—Video Ad Clickedvideo_ad_clickViewer clicked the ad (click-through).
    ad_completeadvideo_ad_completes.tl · event11media.adCompleteVideo Ad Completedvideo_ad_completeAd played to the end.
    ad_break_endadvideo_ad_break_end—media.adBreakCompleteVideo Ad Break Completedvideo_ad_break_endAd pod finished; content resumes.
    volume_changeui————video_volume_changeVolume level changed (debounced).
    muteuivideo_mute—media.statesUpdate—video_muteAudio muted.
    unmuteuivideo_mute—media.statesUpdate—video_unmuteAudio unmuted.
    rate_changeuivideo_rate_change———video_rate_changePlayback speed changed.
    captions_onuivideo_captions—media.statesUpdate—video_captions_onClosed captions enabled.
    captions_offuivideo_captions—media.statesUpdate—video_captions_offClosed captions disabled.
    fullscreen_enteruivideo_fullscreen—media.statesUpdate—video_fullscreen_enterEntered fullscreen (native API or simulated in a device frame).
    fullscreen_exitui——media.statesUpdate—video_fullscreen_exitLeft fullscreen.
    pip_enteruivideo_pip—media.statesUpdate—video_pip_enterEntered picture-in-picture (native) or the floating mini-player.
    pip_exitui——media.statesUpdate—video_pip_exitLeft picture-in-picture or the mini-player.
    theater_onui————video_theater_onTheater (wide) mode on.
    theater_offui————video_theater_offTheater mode off.
    playlist_nextui————video_playlist_nextMoved to the next item (button or autoplay).
    playlist_previousui————video_playlist_previousMoved to the previous item.
    autoplay_toggleui————video_autoplay_toggleAutoplay-next setting changed.
    go_liveseek————video_go_liveLive stream: jumped back to the live edge.
    form_factor_changedevice————video_form_factor_changeSwitched device type (desktop, mobile, tablet, TV, embed).
    orientation_changedevice————video_orientation_changeDevice rotated between portrait and landscape.
    visibility_changedevice————video_visibility_changePage hidden or shown (tab switch, app background).
    errorerrorvideo_errors.tl · event30media.errorVideo Playback Interruptedvideo_errorPlayback, network, DRM, or ad error. Carries code and fatality.
    error_recoverederror——media.play—video_error_recoveredPlayer retried and recovered from a non-fatal error.
    overlay_ad_impressionadvideo_overlay_ad_view——Video Overlay Ad Viewedvideo_overlay_ad_impressionNon-linear overlay banner shown over playing content.
    overlay_ad_clickadvideo_overlay_ad_clicks.tl · event14—Video Overlay Ad Clickedvideo_overlay_ad_clickViewer clicked the overlay banner.
    overlay_ad_closead———Video Overlay Ad Closedvideo_overlay_ad_closeViewer closed the overlay banner.
    companion_impressionadvideo_companion_view——Companion Ad Viewedvideo_companion_impressionCompanion display banner shown beside the player during a linear ad.
    companion_clickadvideo_companion_clicks.tl · event15—Companion Ad Clickedvideo_companion_clickViewer clicked the companion banner.
    pause_ad_impressionadvideo_pause_ad_view——Pause Ad Viewedvideo_pause_ad_impressionPause ad shown while content is paused.
    pause_ad_clickadvideo_pause_ad_click——Pause Ad Clickedvideo_pause_ad_clickViewer clicked the pause ad.
    slate_startslatevideo_slate_start—media.pauseStartVideo Slate Startedvideo_slate_startA slate covers the content (pre-show, technical difficulties, ad filler).
    slate_endslate——media.playVideo Slate Endedvideo_slate_endSlate removed; carries how long it was up.
    chapter_startchaptervideo_chapter_starts.tl · event40media.chapterStartVideo Content Startedvideo_chapter_startPlayhead entered a chapter.
    chapter_completechaptervideo_chapter_complete—media.chapterCompleteVideo Content Completedvideo_chapter_completeChapter played through to its end.
    chapter_skipchaptervideo_chapter_skip—media.chapterSkip—video_chapter_skipViewer left a chapter early by seeking or skipping.
    skip_introengagementvideo_skip_intro——Video Intro Skippedvideo_skip_introViewer pressed Skip intro.
    cta_impressionengagementvideo_cta_view——CTA Viewedvideo_cta_impressionTimed call-to-action shown over the video.
    cta_clickengagementvideo_cta_clicks.tl · event50—CTA Clickedvideo_cta_clickViewer clicked the call-to-action.
    endcard_impressionengagementvideo_endcard_view——Video Recommendations Viewedvideo_endcard_impressionEnd card (watch next + subscribe) shown when content ends.
    endcard_clickengagementvideo_endcard_clicks.tl · event51—Video Recommendation Clickedvideo_endcard_clickViewer picked a recommendation on the end card.
    subscribe_clickengagementvideo_subscribe_clicks.tl · event52—Subscribe Clickedvideo_subscribe_clickViewer clicked Subscribe.
    resume_promptengagement————video_resume_promptOffered to resume from the saved position.
    resume_acceptengagementvideo_resume_from_saved—media.playVideo Resume Acceptedvideo_resume_acceptViewer resumed from the saved position.
    resume_declineengagement———Video Resume Declinedvideo_resume_declineViewer chose to start over.
    shareengagementshares.tl · event53—Video Sharedvideo_shareViewer shared the video (copy link with timestamp).
    likeengagementvideo_likes.tl · event54—Video Likedvideo_likeViewer liked the video.
    unlikeengagement———Video Unlikedvideo_unlikeViewer removed their like.
    cast_startdevicevideo_cast_start——Video Cast Startedvideo_cast_startStarted casting to a TV (simulated Chromecast/AirPlay).
    cast_enddevicevideo_cast_end——Video Cast Endedvideo_cast_endStopped casting; playback returns to this device.
    audio_track_changeui————video_audio_track_changeSwitched audio track (e.g. to audio description).
    gate_showngatevideo_gate_views.tl · event61—Gate Viewedvideo_gate_shownA gate blocked playback (consent, age, registration, geo).
    gate_passedgatevideo_gate_passs.tl · event62—Gate Passedvideo_gate_passedViewer cleared the gate.
    gate_failedgatevideo_gate_fail——Gate Failedvideo_gate_failedViewer did not meet the gate (e.g. under age, outside region).
    gate_dismissedgatevideo_gate_dismiss——Gate Dismissedvideo_gate_dismissedViewer closed the gate without clearing it.
    consent_updateidentitygtag('consent')—setConsentSegment Consent Preference Updatedutag.gdpr.setConsentValueConsent choices changed; vendors re-evaluate what they may send.
    sign_inidentitylogins.tl · event60—identify()user_loginViewer signed in; the anonymous ID is linked to a user ID (identify).
    sign_outidentity———Signed Outvideo_sign_outViewer signed out.
    identity_resetidentityfirst_visit——analytics.resetvideo_identity_resetSimulated a brand-new visitor (new anonymous ID, cookies cleared).
    campaign_arrivalidentitypage_views.t() page view—page()page_viewSimulated landing from a campaign link (UTM parameters).
    page_viewidentitypage_views.t() page view—page()page_viewPage 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.

    1. Artemis I Launches to the Moon (Recap) · HQ · 2022-11-16 · on demand
    2. NASA's Perseverance Rover Lands Successfully on Mars · JPL · 2021-02-18 · on demand
    3. SDO: Year 7 · GSFC · 2017-02-11 · on demand
    4. Earth Views from the International Space Station · HQ · 2019-04-30 · on demand
    5. James Webb Space Telescope: An Overview · GSFC · 2019-03-13 · on demand
    6. Hubble's 29th Anniversary · GSFC · 2019-04-24 · on demand
    7. Can You Spot Hubble: Short · GSFC · 2016-07-21 · used as ad creative
    8. Step Inside the International Space Station (Full Tour) · JSC · 2025-12-17 · simulated live stream
    9. Two Weeks in the Life of a Sunspot · GSFC · 2017-08-04 · on demand