Ever wished you could preview a cycling route before you ride it? We built an automated system that generates beautiful flyover videos for every party route on Party Onbici, giving riders a cinematic preview of what to expect on their journey.

The Challenge

When organizing or joining a cycling event, understanding the route is crucial. Static maps are helpful, but they don’t convey the experience of actually riding the route. We wanted to give users a way to virtually “fly through” the route before committing to a ride.

Our Solution: Headless Video Rendering

We built a Node.js service that runs a headless Chrome browser using Puppeteer, renders the route on an interactive MapLibre GL map, and captures frames as a virtual camera flies along the path. Here’s how it works:

1. Route Interpolation

A cycling route might have thousands of coordinate points. To create a smooth video at 30 FPS for a 15-second duration, we need exactly 450 frames. We use distance-based interpolation to sample the route evenly:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// Calculate cumulative distances using Haversine formula
const distances = [0];
for (let i = 1; i < coordinates.length; i++) {
  const dist = haversineDistance(coordinates[i-1], coordinates[i]);
  totalDistance += dist;
  distances.push(totalDistance);
}

// Sample at exactly totalFrames points
for (let frame = 0; frame < totalFrames; frame++) {
  const targetDist = (frame / (totalFrames - 1)) * totalDistance;
  // Interpolate position at this distance...
}

2. Camera Animation with Smooth Bearing

Simply pointing the camera in the direction of travel creates jerky motion on winding routes. We smooth the camera bearing using a moving average over 12 frames, with special handling for the 360°/0° wraparound:

1
2
3
4
5
6
7
// Use sin/cos components for circular averaging
for (let j = i - smoothWindow; j <= i + smoothWindow; j++) {
  const rad = rawBearings[j] * Math.PI / 180;
  sinSum += Math.sin(rad);
  cosSum += Math.cos(rad);
}
const avgBearing = Math.atan2(sinSum/count, cosSum/count) * 180 / Math.PI;

3. MapLibre GL Rendering

The route is rendered on a beautiful Stadia Maps base layer with:

  • A highlighted route line with white outline for visibility
  • Animated camera position indicator
  • Start (green) and end (red) markers
  • Optional 3D building extrusions for urban areas

4. Frame Capture and Video Encoding

Puppeteer captures each frame as a PNG screenshot, then we use ffmpeg to encode them into a WebM video with VP9 codec:

1
2
3
ffmpeg -framerate 30 -i frame_%05d.png \
  -c:v libvpx-vp9 -crf 20 -b:v 0 \
  route.webm

Technical Architecture

1
Django App → Celery Task → Node.js Renderer → S3 Storage
  1. Django triggers video generation when a party is created or updated
  2. Celery queues the task for async processing
  3. Node.js renderer runs Puppeteer with MapLibre GL
  4. ffmpeg encodes the captured frames
  5. Video is uploaded to S3 and the party record is updated

Performance Considerations

ResolutionDurationRender TimeFile Size
1280x72015s60-90s2-4 MB
1920x108015s90-120s4-8 MB

For production, we use Docker containers with software rendering (SwiftShader) to avoid GPU dependencies:

1
2
--use-angle=swiftshader
--enable-unsafe-swiftshader

Features We Added

  • Customizable video dimensions - Square format for social media sharing
  • Logo watermarking - Brand your videos with your organization’s logo
  • Camera pitch and zoom - Adjust the viewing angle and altitude
  • Route line styling - Custom colors and widths

Django Integration

From the application code, generating a video is as simple as:

1
2
party = Party.objects.get(uid="...")
party.generate_video(width=1080, height=1080, duration=10)

The video URL is automatically updated when rendering completes:

1
2
3
4
5
{% if party.video_url %}
<video controls>
  <source src="{{ party.video_full_url }}" type="video/webm">
</video>
{% endif %}

What’s Next

We’re exploring several enhancements:

  • Real-time preview - Live rendering as users draw routes
  • Multiple camera angles - Side views, overhead shots
  • Weather overlay - Show forecast conditions along the route
  • Elevation profile - Visualize climbs and descents in the video

Route flyover videos have become one of our most popular features, helping cyclists make informed decisions about which rides to join. The combination of modern web technologies makes what would have been an expensive production task into an automated, on-demand service.


Want to try it out? Create a party and your route video will be automatically generated!