How the Steady Camera Movement Adapts Zoom in Google Timeline Visualizer

The steady camera movement adapts zoom by maintaining a fixed 650 km viewing window around the traveler, computing the geographic span of visible points, and applying exponential smoothing with asymmetric alpha values (0.14 when zooming out, 0.035 when zooming in) to ensure fluid transitions.

The mahlernim/google-timeline-visualizer repository provides three built-in camera behaviors—fixed, steady, and dynamic—that control how the map animates during travel route visualization. Unlike the dynamic mode that constantly adjusts the viewport size, the steady camera movement maintains consistent spatial context while smoothly adapting zoom levels based on the geographic spread of points within a fixed-distance window.

Steady Mode Configuration Parameters

The behavior is defined in visualizer.py through a configuration dictionary that establishes geometric constraints and smoothing coefficients. These parameters control the viewing context and animation characteristics:

'steady': dict(
    context_fraction=1.00,          # Use the full route length as context basis

    minimum_context_km=650.0,       # Never look closer than 650 km

    maximum_context_km=650.0,       # Never look farther than 650 km

    padding=2.8,                    # Extra margin multiplier around focused points

    minimum_span=0.00060,           # Smallest allowed map span in degrees

    zoom_out_alpha=0.14,            # Smoothing factor when expanding view

    zoom_in_alpha=0.035,          # Smoothing factor when contracting view

    leg_aware=False,
    fixed_zoom=False,
)

Source: visualizer.py – camera config

The fixed minimum_context_km and maximum_context_km values (both set to 650.0) ensure the camera always evaluates exactly 650 kilometers of the journey centered on the current position, regardless of total route length.

Computing the Raw Zoom Span

The raw_camera_sample function calculates the target geographic span for each animation frame based on the points visible within the steady mode's fixed window. This process involves three steps:

  1. Context window determination – The function calculates tail_distance and lookahead_distance using the context_fraction and fixed 650 km limits, establishing how far behind and ahead of the current position to include (visualizer.py L47-L49).

  2. Geographic bounds calculation – All map points within the window (including the current position) are collected, and their X and Y ranges are determined.

  3. Span computation – The raw span equals the larger of the X-range or Y-range multiplied by the padding value (2.8), ensuring adequate margin around the route. This value is clamped to never fall below minimum_span (0.00060 degrees) (visualizer.py L71-L76).

The function returns a tuple of (center_x, center_y, span) representing the viewport center and the raw zoom level required to display all points in the current window.

Smoothing the Zoom Transition

The build_camera_track function processes these raw spans through exponential smoothing to eliminate jarring jumps while the traveler moves through varying terrain densities. This smoothing algorithm distinguishes between zooming in and zooming out using different response rates:

Asymmetric Alpha Selection – For each successive frame, the algorithm compares the target span against the previous frame's span (visualizer.py L601-L603):

  • Zooming out (target > previous): Uses zoom_out_alpha = 0.14 for faster expansion when the route spreads out
  • Zooming in (target < previous): Uses zoom_in_alpha = 0.035 for slower, more deliberate contraction

Exponential Interpolation – The new span calculates through logarithmic interpolation to maintain perceptually smooth motion:

span = math.exp(
    math.log(previous_span) +
    (math.log(target_span) - math.log(previous_span)) * alpha
)

Source: visualizer.py L602-L604

This mathematical approach ensures the steady camera movement adapts zoom gradually, reacting to changes in route topology while maintaining the fixed 650 km spatial context. Because the window size remains constant, zoom changes stem solely from the traveler's speed and natural point distribution within the viewing area.

Implementation Example

To utilize the steady camera movement in your visualization pipeline, invoke the build_camera_track function with the movement_name parameter set to 'steady':


# Build a camera track with the steady movement

track = build_camera_track(
    cum_dist,          # cumulative distance array in kilometers

    xs, ys,            # mercator X/Y coordinate arrays

    lats, lons,        # original latitude/longitude arrays

    movement_name='steady',
    distance_at=build_journey_timing(cum_dist, 'balanced'),  # timing curve

)

# Sample frames to observe zoom span evolution

for i in range(0, len(track), len(track)//5):
    cx, cy, span = track[i]
    print(f'Frame {i:03d}: center=({cx:.0f},{cy:.0f})  span={span:.0f} m')

Relevant source: build_camera_track implementation – visualizer.py L80-L106

Summary

  • The steady camera movement maintains a fixed 650 km viewing window around the current position using matching minimum_context_km and maximum_context_km values.
  • Raw zoom spans compute from the geographic spread of points within this window, multiplied by a padding factor of 2.8 and constrained by a minimum span threshold.
  • Exponential smoothing with asymmetric alpha values (0.14 for zoom out, 0.035 for zoom in) ensures fluid transitions without abrupt changes.
  • The implementation resides primarily in visualizer.py, specifically within the raw_camera_sample and build_camera_track functions.

Frequently Asked Questions

Why does the steady mode use different alpha values for zooming in and out?

The steady camera movement prioritizes visual stability by zooming out quickly (alpha = 0.14) when the route expands to ensure viewers never miss geographic context, while zooming in slowly (alpha = 0.035) to avoid disorienting rapid contractions. This asymmetry creates a more comfortable viewing experience during variable terrain traversal.

How does the 650 km window affect the zoom adaptation behavior?

Because minimum_context_km and maximum_context_km are both set to 650.0 with context_fraction=1.0, the camera always evaluates exactly 650 kilometers of the route centered on the current position. This fixed window means zoom changes depend entirely on how geographically spread out the points are within that constant distance, rather than dynamically adjusting the viewing radius based on total route length.

What happens if the route points are very densely packed within the 650 km window?

When coordinates cluster tightly within the viewing window, the raw span calculation produces small values near the minimum_span threshold (0.00060 degrees). The padding multiplier (2.8) ensures adequate breathing room around dense clusters, while the exponential smoothing prevents micro-jitter by gradually interpolating between frames even when point distributions change rapidly.

Can the steady camera parameters be customized for different visualization needs?

Yes, the configuration dictionary in visualizer.py (lines 69-72) exposes all parameters including context_fraction, minimum_context_km, padding, and the alpha values. Adjusting these values changes the spatial context size, margin around routes, and zoom transition speeds, though modifying the alpha values requires understanding that they represent blending factors in logarithmic space rather than linear percentages.

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 →