How Journey Timing and Compression Work in the Google Timeline Visualizer

The Google Timeline Visualizer converts geographic journey data into compressed MP4 videos by calculating frame-level progress interpolation based on user-defined durations, while dynamically selecting H.264 codecs and applying bitrate constraints through the mediabunny encoding pipeline.

The mahlernim/google-timeline-visualizer repository transforms PreparedJourney objects—sequences of geo-points rendered as map tiles—into browser-encoded video files. According to the source code in web/src/video.ts, the export pipeline cleanly separates timing logic (frame generation and progress tracking) from compression mechanics (codec negotiation and bitrate management) to produce optimized MP4 outputs directly within the browser.

Journey Timing Mechanics

The visualizer calculates video timing by mapping wall-clock duration to discrete frames, then interpolating progress values that drive the map renderer.

Frame Count Calculation

The createJourneyMp4 function determines the total number of frames by multiplying the user-specified durationSeconds by the chosen format's frame rate. As implemented in web/src/video.ts, the calculation guarantees at least one frame regardless of duration:

const journeyFrameCount = Math.max(1, Math.round(options.durationSeconds * fps));

The pipeline allocates frames between the active journey segment and a fixed outro sequence. The outro duration derives from OUTRO_SECONDS defined in web/src/animation.ts, converted to frames using the same FPS value. This separation ensures the camera animation concludes smoothly after the path traversal completes.

Progress Interpolation

For each frame index, the visualizer constructs a TimelineFrame object containing normalized progress values. During the journey portion, journeyProgress interpolates linearly from 0 to 1 using the formula frame / (journeyFrameCount - 1). Once the journey frames conclude, frameAtElapsedSeconds (from web/src/animation.ts) calculates outroProgress to drive fade-out animations and final camera positioning. The drawFrame method in web/src/renderer.ts consumes these progress values to render the correct map viewport and overlay state for each video frame.

Video Compression Pipeline

Compression is handled through dynamic codec probing and configurable bitrates passed to the WebCodecs API via the mediabunny library.

Codec Selection and Probing

Rather than assuming codec availability, the visualizer probes the browser's VideoEncoder capabilities at runtime. The probeCodec function tests candidate profiles in order of preference—typically Baseline (avc1.42001f) followed by High profile variants—using resolveCodecString to select the first supported configuration. This ensures maximum compatibility across devices while preferring hardware-accelerated encoding when available.

Bitrate Configuration

Each video format definition in web/src/video.ts specifies a target bitrate in bits per second. Presets range from approximately 2.5 Mbps for standard quality to 12 Mbps for ultra-quality exports. The resolved bitrate feeds into a Quality object instantiated as new Quality({ bitrate }), which mediabunny uses to configure the encoder's compression algorithm. Higher bitrates preserve finer map detail during fast camera movements but increase the final MP4 file size.

Integrating Timing and Compression in the Export Flow

The createJourneyMp4 function orchestrates both systems. It accepts an ExportOptions object containing the duration, format (with FPS and bitrate), and overlay metadata, then initializes a CanvasSource with the resolved AVC codec string and quality settings.

The export sequence proceeds as follows:

  1. Initialize Encoder: Create a CanvasSource with codec: 'avc' and the probed codec string, plus the Quality configuration derived from the format's bitrate.
  2. Generate Frames: Iterate from 0 to journeyFrameCount + outroFrameCount, calling drawFrame for each index to render the canvas state based on journey and outro progress.
  3. Encode Stream: Immediately pass each drawn canvas frame to source.add() for hardware-accelerated encoding.
  4. Finalize: Call output.finalize() to flush the encoded bitstream and return the compressed MP4 Blob.

Practical Configuration Examples

To export a 30-second journey with standard compression:

const options: ExportOptions = {
  durationSeconds: 30,
  overlay: { title: 'My Trip', subtitle: '' },
  format: resolvedFormat,  // Contains fps and target bitrate
  onProgress: (pct) => console.log(`${pct}% complete`),
};

const blob = await createJourneyMp4(canvasElement, preparedJourney, options);
const file = new File([blob], 'journey.mp4', { type: 'video/mp4' });

To prioritize quality over file size, select a higher-bitrate format before probing:

const support = await probeVideoFormats();
const resolved = resolveVideoFormat('ultra', support);  // 12 Mbps target

if (resolved) {
  options.format = resolved;  // bitrate and codec candidates updated
}

Even sub-second durations generate valid outputs due to the Math.max(1, ...) guard in the frame calculation:

options.durationSeconds = 0.5;  // Still produces journeyFrameCount = 1

Summary

  • Timing Logic: Frame counts derive from durationSeconds * fps with a minimum of one frame, split between journey animation and fixed-duration outro sequences defined in web/src/animation.ts.
  • Progress Tracking: Each frame carries journeyProgress (0–1) and outroProgress values calculated in web/src/video.ts that control camera and overlay rendering.
  • Codec Negotiation: The visualizer probes for H.264 support (Baseline to High profiles) at runtime via probeCodec to ensure hardware compatibility.
  • Bitrate Control: Quality is determined by format-specific bitrates (2.5–12 Mbps) passed to mediabunny's Quality constructor, balancing visual fidelity against file size.
  • Pipeline Architecture: createJourneyMp4 in web/src/video.ts coordinates canvas rendering, frame encoding, and MP4 multiplexing without server-side processing.

Frequently Asked Questions

How does the visualizer calculate the number of frames for a specific journey duration?

The system multiplies the user-provided durationSeconds by the selected format's frame rate (FPS), then rounds to the nearest integer while enforcing a minimum of one frame via Math.max(1, Math.round(...)). This calculation occurs in createJourneyMp4 within web/src/video.ts to ensure that even very short clips render correctly.

What video codec is used and how is it selected?

The visualizer targets H.264 (AVC) encoding using the WebCodecs API. At runtime, probeCodec tests predefined candidate strings (such as avc1.42001f for Baseline and avc1.64001f for High profile) against the browser's VideoEncoder.isConfigSupported method, selecting the first compatible option to maximize hardware acceleration support.

How is video quality controlled during compression?

Quality is governed by the bitrate parameter defined in each video format preset (e.g., 2.5 Mbps for standard, 8–12 Mbps for ultra). This value passes to mediabunny as a Quality object, which configures the encoder's bit allocation. Higher bitrates retain more detail in the map tiles during motion but result in larger MP4 files.

Can the journey duration and compression settings be customized independently?

Yes. The ExportOptions interface accepts durationSeconds separately from the format object, which encapsulates both FPS and bitrate. You can specify a 5-second journey with ultra-quality compression (high bitrate) or a 60-second journey with standard compression, as the timing and compression pipelines operate on separate input parameters within createJourneyMp4.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →