vibecodingtools.net

► Build a Retro Screensaver

A single HTML file that brings back the screensavers everyone remembers from the 1990s — built with nothing but HTML, CSS, and a small amount of JavaScript.

// Live Preview

► Open Fullscreen ↓ Download screensaver.html

// How to Build This

Six steps. No libraries, no build tools — just HTML, CSS, and JavaScript.

01

Gather Your Video Clips

Collect short .mp4 files — anywhere from 5 to 30 seconds each works well. The screensaver plays them in a randomised order and makes sure every clip plays before anything repeats.

Put all your video files in the same folder as your HTML file. No server setup needed — it runs directly from your file system.

my-screensaver/
  index.html       <-- your code lives here
  clip1.mp4
  clip2.mp4
  clip3.mp4
  ...
02

The HTML — Three Elements

The whole thing runs on two video elements and one canvas. One video plays while the other loads the next clip silently in the background. When it is ready, they swap — and the canvas draws a pixelated blend over the top during the crossfade.

<!-- Two videos sit on top of each other -->
<video id="vidA" muted playsinline></video>
<video id="vidB" muted playsinline></video>

<!-- Canvas draws the pixel effect on top during transitions -->
<canvas id="px-canvas"></canvas>
03

CSS — Cover the Screen

All three elements sit on top of each other and fill the full screen. Visibility is controlled with opacity alone — nothing is hidden or removed, just faded in and out. The canvas gets one extra rule that keeps pixels hard-edged when it is scaled up.

/* Fill the screen, stack on top of each other */
video, canvas {
  position: absolute;
  inset: 0;           /* shorthand for top/right/bottom/left: 0 */
  width: 100%;
  height: 100%;
  object-fit: cover;  /* fills frame without stretching */
  opacity: 0;          /* hidden by default, faded in by JS */
}

/* Crisp pixels when the canvas is scaled up */
canvas {
  image-rendering: pixelated;
}
04

Randomised Playlist

Every clip should play once before anything repeats — and the order should be different each time. A standard shuffle algorithm handles this in about ten lines. When the playlist runs out, it reshuffles and starts again.

const VIDEO_FILES = ['clip1.mp4', 'clip2.mp4', 'clip3.mp4'];

// Shuffle so every clip plays before any repeats
function shuffle(arr) {
  const a = [...arr];
  for (let i = a.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [a[i], a[j]] = [a[j], a[i]];
  }
  return a;
}

let playlist  = shuffle(VIDEO_FILES);
let listIndex = 0;

function nextClip() {
  listIndex++;
  if (listIndex >= playlist.length) {
    playlist  = shuffle(VIDEO_FILES); // reshuffle when exhausted
    listIndex = 0;
  }
  return playlist[listIndex];
}
05

The Crossfade

While the current clip is playing, the next one loads silently into the background video element. When it is time to switch, the browser animates the opacity of both elements over about one second — one fades out while the other fades in. When it is done, the outgoing video is cleared and the two elements swap roles.

function startTransition() {
  incoming.src = nextClip();
  incoming.play();

  const startTime = performance.now();

  function tick(now) {
    const t = Math.min((now - startTime) / 1200, 1); // 0 → 1

    outgoing.style.opacity = 1 - t; // fades out
    incoming.style.opacity = t;       // fades in

    if (t < 1) {
      requestAnimationFrame(tick);
    } else {
      outgoing.pause();
      // swap which video is "active" for next time
    }
  }
  requestAnimationFrame(tick);
}
06

The Pixel Effect

During the crossfade, both videos are drawn onto a very small canvas — roughly 96×54 pixels. That small canvas is then scaled back up to fill the screen with image smoothing turned off, which produces hard pixel edges instead of a blurry blend. The result is the blocky, chunky look that made 90s graphics feel the way they did.

The pixel overlay fades in at the start of the transition, peaks in the middle, and fades out at the end — so it only appears during the switch, not while a clip is playing cleanly.

// A tiny canvas scaled up gives you pixel blocks for free
const offscreen = document.createElement('canvas');
offscreen.width  = 96;  // smaller = bigger blocks
offscreen.height = 54;

function drawPixelFrame(t) {
  // Blend both videos at low resolution
  offCtx.globalAlpha = 1 - t;
  offCtx.drawImage(outgoing, 0, 0, 96, 54);
  offCtx.globalAlpha = t;
  offCtx.drawImage(incoming, 0, 0, 96, 54);

  // Scale up without smoothing — hard pixel edges
  ctx.imageSmoothingEnabled = false;
  ctx.drawImage(offscreen, 0, 0, canvas.width, canvas.height);
}

// Opacity follows a sine arc — peaks at the midpoint of the transition
canvas.style.opacity = Math.sin(t * Math.PI);

► Get the Full Code

All six steps combined into a single file — open it in any browser and it runs immediately.

↓ Download retro-screensaver.html