Back to the blog
  • astro
  • threejs
  • architecture

This site is a khipu

Why I told my career with cords and knots, and how it's built: Astro, Three.js, rope physics and an arcade made with agents.

The home khipu: seven dyed cords hanging from a primary cord, one per company.

Khipus were the Andean record-keeping system: a primary cord, pendant cords and knots. A knot’s position marked units, tens or hundreds, and the number of turns in a long knot was the digit. Structured data, no paper.

When I set out to rebuild this site after four years, I realized my career already had that shape: companies are cords, stages are subsidiary cords and deliveries are knots.

How to read it

  • One cord per company. When I held several stages at the same company, each stage hangs as a subsidiary cord. Auna, for example, has three.
  • Single knots for things that reached production.
  • Long knots for numbers: the 30–40% saving at TopSort is a three-turn knot.
  • Top cords for artifacts I built across companies.
The API Gateway shows up at five companies: the top cord ties them all together.

Data first

The whole site comes from one typed file. The 3D khipu, the chapters, the flat view and the PDF resume read the same data, so there are never two versions of the truth.

src/data/career.ts

export interface Knot {
  id: string;
  /** 1 = single knot; more than 1 = long knot whose turns are a digit */
  n: number;
  text: { es: string; en: string };
  metric?: string;
}

const finops: Knot = {
  id: "finops",
  n: 3,
  metric: "30–40%",
  text: {
    es: "30–40% menos costo de infraestructura con FinOps, Karpenter y KubeCost.",
    en: "30–40% lower infrastructure cost with FinOps, Karpenter and KubeCost.",
  },
};

Rope physics

Each cord is a chain of points integrated with Verlet. Every frame applies gravity, moves each point by its implicit velocity, then corrects the distance between neighbors a few times:

for (let it = 0; it < 10; it++) {
  rope.pts[0].copy(anchor);
  for (let i = 0; i < SEG; i++) {
    const p = rope.pts[i], q = rope.pts[i + 1];
    const d = p.distanceTo(q) || 1e-6;
    const k = ((d - rope.segLen) / d) * (i === 0 ? 1 : 0.5);
    // pull or push both ends back to the rest length
  }
}

The arcade and the agents

The arcade has nine 3D games. Each was built by an independent agent following a shared contract: the game only emits events, and the shared cabinet handles best scores and achievements.

ctx.emit({ type: "score", value: 1200 });
ctx.emit({ type: "stat", key: "bugs", inc: 1 });
ctx.emit({ type: "gameover", score: 1200 });

How it ships

Astro generates everything as a static site in Spanish and English, and GitHub Actions builds and publishes to GitHub Pages on every push to main. No servers to maintain.

Written by

Luis Diego

Engineering Manager and Principal Architect. I write about architecture, platform and AI.

Let's talk