Notes on how this site works. Not a tutorial — just documentation of choices made and why. Very incomplete. I'll add more as I go.
Canvas Animations
Several posts on str.is include animated visualizations. These aren't GIFs, videos, or embedded content from external services. They're HTML5 Canvas animations rendered in real-time by your browser.
Examples
- /4/ — Floating ASCII trees and words (train window effect), glitching boolean values
- /5/ — Swimming dragon, drone with XYZ coordinates, AI swarm processing cases from an inbox
How It Works
Each animation follows the same basic pattern:
<canvas id="myCanvas"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
function animate() {
// Clear the canvas
ctx.fillStyle = '#0d0d0d';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw something
ctx.fillStyle = '#00ff00';
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
// Update positions for next frame
x += velocity;
// Request next frame (~60fps)
requestAnimationFrame(animate);
}
animate();
</script>
The key function is requestAnimationFrame(). It tells the browser: "call this function again before the next repaint." This creates a loop running at approximately 60 frames per second, synchronized with the display's refresh rate.
Why Canvas Instead of GIF?
GIF
- Fixed resolution — blurry on retina
- 10-15 fps typical
- Large file size for smooth animation
- 256 color limit
- Loops forever, can't pause
Canvas (preferred)
- Responsive — scales to any size
- 60 fps, smooth
- Tiny file size (just code)
- Full color spectrum
- Auto-pauses when tab inactive
The last point matters more than you'd think. requestAnimationFrame() automatically stops when the browser tab isn't visible. GIFs keep running, burning CPU in background tabs. Canvas is polite.
Performance
These animations are lightweight. A few dozen shapes per frame, simple math (sin(), cos(), addition), no physics simulations or collision detection. Modern browsers offload Canvas 2D rendering to the GPU.
What would make it heavy:
- Thousands of particles
- Blur or shadow effects (expensive)
- Image manipulation every frame
- Complex physics with collision detection
None of the animations on this site do any of that. CPU usage stays around 1-3% on a typical machine.
A Minimal Example
Here's a simple bouncing dot — the core mechanic used in the /4/ train window:
let x = 50, y = 50;
let vx = 2, vy = 1.5;
function animate() {
ctx.fillStyle = '#0d0d0d';
ctx.fillRect(0, 0, w, h);
// Move
x += vx;
y += vy;
// Bounce off edges
if (x < 0 || x > w) vx *= -1;
if (y < 0 || y > h) vy *= -1;
// Draw
ctx.fillStyle = '#00ff00';
ctx.beginPath();
ctx.arc(x, y, 6, 0, Math.PI * 2);
ctx.fill();
requestAnimationFrame(animate);
}
That's it. Position, velocity, boundary check, draw, repeat. Everything else is variation on this theme.
View Source
Here's the thing: you can read all of it. Right-click any page, View Source, and the animation code is right there in the HTML. No build step, no minification, no webpack, no mystery.
Want to see how the dragon in /5/ works? View source, scroll to <script>, read the code. Want to adapt it for your own site? Copy it. That's the point.
This isn't a framework or a library. It's just JavaScript doing what JavaScript has always been able to do — drawing shapes on a canvas, moving them around, repeating 60 times per second.
Why Bother?
Four reasons:
- It's memorable. Static text is forgettable. Movement catches attention and sticks.
- Form matches content. When writing about swarms and coordination, showing a swarm is more effective than describing one.
- It signals capability. Anyone can embed a YouTube video. Building your own visualizations says something different.
- It's transparent. No black boxes. View source and learn. The web used to work this way.
The animations aren't decoration. They're communication.
Zero Cookies
str.is sets zero cookies. None. Not "just analytics." Not "only essential." Zero.
You can verify this yourself. Open DevTools, go to Application → Cookies → str.is. Empty. No cookie banner needed because there's nothing to consent to.
What About localStorage?
Some sub-sections (like swarm.str.is) use localStorage for:
- Theme preference (dark/modern)
- Language preference (Norwegian/English)
- Auth tokens (when you choose to log in)
localStorage is fundamentally different from cookies:
Cookies
- Sent to server with every request
- Can track you across sites
- Server can set them without asking
- Requires GDPR consent banners
localStorage
- Never leaves your browser
- Site-specific, can't track across domains
- Only JavaScript can access it
- Your data stays on your machine
When you pick a theme on swarm.str.is, that preference is stored locally on your device. The server never knows. We never know. It's yours.
No Analytics Either
No Google Analytics. No Facebook Pixel. No Plausible, Fathom, or "privacy-friendly" alternatives. No tracking scripts of any kind.
This means I have no idea how many people visit this site. I don't know which posts are popular. I can't see where traffic comes from. And that's fine.
The trade-off is worth it. You get a site that loads fast, doesn't phone home, and respects your attention. I get to build things without obsessing over metrics.
Why?
Because the web became a surveillance machine, and I don't want to contribute to that.
Every cookie banner is an admission: "We're doing something you wouldn't agree to if you understood it." Every "legitimate interest" checkbox is a dark pattern. Every "we value your privacy" popup appears on sites that manifestly do not.
The simplest way to respect privacy is to not collect data in the first place. No data, no breaches. No tracking, no consent theater. No analytics, no temptation to optimize for engagement over substance.
This is what the web looked like before AdTech ate it. Just pages. Just content. Just view-source: and everything right there.
More sections will be added as the site evolves.