Unerdwear

Developer tools · lightweight guides · security notes

Bacon Screensaver (2018)

Bacon slices arranged on a light background
Source: Unsplash.

This is a tiny joke project with a serious constraint: keep it safe. Anything that looks like a “screensaver download” is a magnet for sketchy bundlers and weird executables, so the clean approach is to make a local animated page you can run yourself.

The end result is still the same vibe: bacon floating around the screen while you’re away from the keyboard.

What we’re making

A screensaver-style animation has three parts:

  1. A full-screen view.
  2. A loop that updates positions over time.
  3. A way to exit quickly (any key or mouse movement).

We can get 90% of that with a browser in full-screen mode and a local HTML file.

Assets: keep it simple (and make your own)

You only need a few bacon images (PNG or WebP with transparency). If you want to draw your own:

Aim for 2–6 variations so it doesn’t feel too repetitive.

A safe modern remake: local HTML + CSS + JS

Save this as bacon.html in a folder next to your image files (b1.png, b2.png, b3.png, etc). Then open it in a browser.

<!doctype html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bacon</title>

<style>
  html, body { height: 100%; }
  body { margin: 0; background: #111; overflow: hidden; }
  .b {
    position: absolute;
    width: 240px;
    opacity: .85;
    box-shadow: 0 18px 30px rgba(0,0,0,.45);
    will-change: transform;
    user-select: none;
    pointer-events: none;
  }
</style>

<script>
  const images = ["b1.png", "b2.png", "b3.png"]; // local files
  const count = 18;
  const items = [];

  function rand(min, max) { return Math.random() * (max - min) + min; }

  for (let i = 0; i < count; i++) {
    const el = document.createElement("img");
    el.className = "b";
    el.alt = "bacon";
    el.src = images[i % images.length];
    document.body.appendChild(el);

    items.push({
      el,
      x: rand(0, window.innerWidth),
      y: rand(0, window.innerHeight),
      speed: rand(15, 55),
      drift: rand(0.3, 1.2),
      rot: rand(-40, 40),
      phase: rand(0, Math.PI * 2)
    });
  }

  let last = performance.now();

  function frame(now) {
    const dt = Math.min(0.05, (now - last) / 1000);
    last = now;

    for (const it of items) {
      it.phase += dt * it.drift;
      it.y += dt * it.speed;
      it.x += Math.sin(it.phase) * 20 * dt;

      if (it.y > window.innerHeight + 260) {
        it.y = -260;
        it.x = rand(0, window.innerWidth);
      }

      it.el.style.transform = `translate(${it.x}px, ${it.y}px) rotate(${it.rot}deg)`;
    }

    requestAnimationFrame(frame);
  }

  requestAnimationFrame(frame);

  window.addEventListener("resize", () => {
    // Keep things bounded when the screen size changes.
    for (const it of items) {
      it.x = Math.min(it.x, window.innerWidth);
      it.y = Math.min(it.y, window.innerHeight);
    }
  });
</script>

Why requestAnimationFrame? It syncs updates to the browser’s paint loop and usually behaves better than timers for animation.

Running it like a screensaver

This depends on your OS and personal preference, but the simplest approach is:

If you want it to feel more “screensaver-like”, keep the page minimal: no UI, no links, no external resources.

Last updated: 2026-01-02