# How Journey Timing and Compression Work in the Google Timeline Visualizer

> Discover how Google Timeline Visualizer uses journey timing and compression with frame interpolation and H.264 codecs. Learn about the mediabunny encoding pipeline for efficient video creation.

- Repository: [mahlernim/google-timeline-visualizer](https://github.com/mahlernim/google-timeline-visualizer)
- Tags: internals
- Published: 2026-08-22

---

**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`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/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`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/web/src/video.ts), the calculation guarantees at least one frame regardless of duration:

```typescript
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`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/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`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/web/src/animation.ts)) calculates `outroProgress` to drive fade-out animations and final camera positioning. The `drawFrame` method in [`web/src/renderer.ts`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/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`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/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:

```typescript
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:

```typescript
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:

```typescript
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`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/web/src/animation.ts).
- **Progress Tracking**: Each frame carries `journeyProgress` (0–1) and `outroProgress` values calculated in [`web/src/video.ts`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/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`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/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`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/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`.