video playback on zero-G budget (S3 + CloudFront)

Home Feed
2026-01-11 aiawscloudflareffmpeg

This template is designed for low-cost ergonomic websites. The objective is instant playback with bounded worst-case egress and minimal infrastructure. This is how I've optimised https://gaurv.me/🎰 to serve Pink Floyd's music (no strikes).

I won't discuss blocking bots or CAPTCHA verification. You can get decent coverage by hosting on Cloudflare for free, though I'm not claiming it works 100% of the time as advertised. But it's better than nothing. Once you've figured this out, lets see how we can serve heavy videos in most cache-efficient ways and budget contraints. Workflow has been written using AI with parts of this post edited by me.

encode assets

full video

Use a single MP4 file optimized for progressive playback. Do not segment.

bash 🔗
ffmpeg -i input.mp4 \
  -c:v libx264 -crf 22 -preset slow \
  -c:a aac -b:a 128k \
  -movflags +faststart \
  full.mp4

While most browsers (albiet with some nuances) support range requests, it's safer to move video metadata to the beginning. faststart is the conventional way to guarantee this.

preview video

Modern browsers do not request entire video at once but in ranges so playback can start immediately. By creating a preview we only serve the full video if and only if the viewer wants.

bash 🔗
ffmpeg -i input.mp4 \
  -ss 0 -t 15 \
  -vf scale=640:-2 \
  -c:v libx264 -profile:v baseline -level 3.0 \
  -b:v 600k -g 30 \
  -c:a aac -b:a 96k \
  -movflags +faststart \
  preview.mp4

Note: If you are comfortable with the fact that any page view that reaches play can result in the full video being streamed, then you do not need a preview.

upload to S3

Use a single bucket for all video assets. No bucket policy changes are required. Create separate folders and upload the assets.

/preview/preview.mp4
/full/full.mp4

Why different folders? To set object-level metadata.

Preview object:

Cache-Control: public, max-age=31536000, immutable
Content-Type: video/mp4

Full object:

Cache-Control: public, max-age=86400
Content-Type: video/mp4

CloudFront configuration

Use a single CloudFront distribution.

Origin configuration:

Behaviors:

Do not use HLS or DASH. A single MP4 provides better cache efficiency and lower request volume.

video attributes

Never reference the full video during initial render. The video element must be created without a source.

html 🔗
<video id="video" playsinline preload="metadata" poster="/poster.webp" controls></video>
<button id="play-full">Play Full</button>

intent-gated loading

Attach the preview only when playback starts. Attach the full video only after explicit user action.

js 🔗
const v = document.getElementById("video");

v.addEventListener("play", () => {
  if (!v.src) {
    v.src = PREVIEW_URL;
    v.load();
  }
});

playFullBtn.onclick = () => {
  v.src = FULL_URL;
  v.load();
  v.play();
};

Rules:

cost control invariants

The following invariants must always hold.

Page load must trigger zero video bytes. The preview must cap accidental egress. The full video must load only on explicit user action. Use a single MP4 file without segmentation. Apply long cache TTLs to previews and short TTLs to full assets.

Bottom Line

Playback speed comes from faststart and CDN caching. Cost control comes from intent gating. These concerns are separate and must not be conflated.

Verify behavior in browser DevTools:

On page load, no MP4 requests should appear. On first play, only the preview file should be requested. On full playback, the full MP4 should begin streaming. Responses should be 206 Partial Content with x-cache: Hit from CloudFront.