JS
Back to Blog

Recreating Warframe's Infuriating Hacking Minigame

How I rebuilt the Corpus hex-rotation puzzle in PixiJS: bitfields, grid math, and a fully playable endless demo.

·8 min read·
Warframe
PixiJS
Game Dev
JavaScript
Recreating Warframe's Infuriating Hacking Minigame

The puzzle that broke a thousand Tenno

If you've played Warframe at all, you know the Corpus hacking minigame. Seven hexagons. Lines that need to connect. A timer that starts counting down the moment you make your first move. Most of the time it's trivial. On high-level Corpus missions, the outer ring fills up and suddenly it stops being trivial.

The puzzle looks deceptively simple: click a hexagon to rotate it clockwise. Right-click to go counter-clockwise. Get all the lines to connect. Done.

Under the hood, it's a neat little puzzle with a clean data model worth unpacking.

If you haven't played Warframe, it's a free-to-play co-op shooter with a surprising amount of depth. Worth checking out on Steam.

The bitfield trick

Each hexagon has six edges. Each edge either has a connection or it doesn't. That's six bits of information, so the state of any hexagon fits in a single integer from 0 to 63.

// Rotating clockwise shifts all bits left by one, wrapping bit 5 → bit 0
function cw(connBits) {
  return (connBits << 1) & 63 | (connBits & 32 ? 1 : 0);
}
 
// Counter-clockwise is the reverse
function ccw(connBits) {
  return (connBits >> 1) | (connBits & 1 ? 32 : 0);
}

Rotation is just a bit shift. No trig, no angle math. The visual rotation of the hexagon on screen is separate from the logical state - it's just an animation that lerps back to zero.

The hex grid

The seven hexagons sit in a flat-top hex grid. Their world positions are computed from integer grid coordinates:

var hexEdge = Math.sqrt(3 / 4); // ≈ 0.866
 
cx = gx * rad * 2 * hexEdge + gy * rad * hexEdge;
cy = gy * rad * 1.5;

Each of the six edges maps to a direction in grid space:

// connBit → neighbour offset
// 1  → (+1,  0)
// 2  → ( 0, +1)
// 4  → (-1, +1)
// 8  → (-1,  0)
// 16 → ( 0, -1)
// 32 → (+1, -1)

When two hexagons are adjacent, the connection bit on each side is the bitwise reverse of the other. That reverse mapping is a single operation:

function reverseBit(connBit) {
  return (connBit >> 3) + ((connBit & 7) << 3);
}

The solve check

At any point you can check whether the board is solved by iterating every hexagon and verifying that each active connection bit has a corresponding reverse bit in the neighbour:

function checkBoard() {
  var solved = true;
  forEachHexagon(function (hex) {
    for (var bit = 1; bit & 63; bit <<= 1) {
      if (hex.connBits & bit) {
        var neighbour = getNeighbour(hex, bit);
        if (!neighbour || !(neighbour.connBits & reverseBit(bit))) {
          solved = false;
        }
      }
    }
  });
  return solved;
}

This runs every time a hexagon rotates. On a 7-hex board it's a handful of integer operations.

The outside-in strategy

The reason the puzzle is solvable reliably comes down to one insight: the center hex has no unconstrained edges. Every connection from the center must connect to the ring, and every ring hex must connect either to the center or to its ring neighbours.

The practical strategy follows from this:

  1. Ignore the center hex entirely. Start with any ring hex that has a "wide angle" (connections on two adjacent edges pointing outward).
  2. Rotate it so those connections face inward toward the center.
  3. Move clockwise around the ring. Each subsequent hex has at most one ambiguous connection - you can resolve it by looking at the hex you just solved.
  4. The only real decision point is a single-line hex with no clear direction. Jump ahead to the next wide-angle hex and work back.
  5. Once the ring is done, click the center hex until it snaps into place. It will solve instantly because the ring already constrains every possible position.

Building it in PixiJS

Each hexagon is two layers: a staticGfx (outline, never moves) and an inner container (rings and connection lines, rotates). Keeping them split means the border stays crisp during animation - only the inner content spins.

Connection lines point to edge midpoints using a cosine/sine offset from center:

var lineR = rad * hexEdge * hexEdge;
var angle = (Math.PI / 3) * i;
var ex = lineR * Math.cos(angle);
var ey = lineR * Math.sin(angle);

Each bit in connBits maps to one of six directions. A square dot sits at 75% along each active line to show connection endpoints.

Touch input uses a long-press pattern: a 400ms timer starts on pointerdown. Release before it fires: short tap (clockwise). Timer fires first: long-press (counter-clockwise). This gives the same left-click / right-click model on desktop without separate controls.

The rotation animation eases out exponentially rather than linearly:

h.rot *= Math.pow(0.00001, dt);

This gives a fast initial snap that decelerates to a near-zero crawl, matching the Warframe feel. Rapid clicks in opposite directions accumulate rather than snap, so you can reverse direction mid-animation cleanly.

The difficulty progression mirrors the original: each board removes a random number of connections from the grid. After every three solves, removeMin and removeMax tighten - fewer removed connections means more ambiguous hexes. Once both hit their minimum, the board grows to the next size tier (7 → 13 → 19 hexagons).

Play it

Tap to rotate clockwise. Long-press or right-click for counter-clockwise. The board expands from 7 hexagons to 13, then 19 as you keep solving.

Click to play

What I added beyond the original

The original Corpus hack is a pure puzzle with no score, no timer pressure. Cipher is the only relief valve: a craftable consumable that auto-solves the board instantly. Here's what the demo layers on top.

Score and time pressure

Each board solved earns a base 1000 points plus a speed bonus of up to 500:

var timeBonus = Math.round(500 * Math.max(0, (HUD_TIME - timeTaken) / HUD_TIME));
totalScore += 1000 + timeBonus;

The timer budget starts at 20 seconds and drops 0.5 seconds per solve, flooring at 12 seconds. The per-second pulse on the countdown uses a spring system so it breathes rather than snaps.

Seeded puzzles with IDs

Every board is seeded using a mulberry32 PRNG. The seed is displayed as a puzzle ID (bottom-left) so you can report a specific layout. The solved state is stored before scrambling so the Cipher always knows the exact target rotation for each hex.

Cipher

Cipher exists in Warframe as a craftable consumable that instantly solves the hack. The demo keeps that scarcity: you start with 3, earn one back every 5 solves. The implementation finds the shortest rotation path for each hex (CW or CCW, whichever needs fewer steps) and fires them staggered for visual effect rather than snapping instantly.

Techrot

A new addition not in the original. Immediately jumps the board to the next difficulty tier. One use per run. In the actual game, Techrot is an enemy faction that corrupts Corpus systems, felt fitting as a name for something that makes your life harder.

Difficulty border

The animated border around the canvas changes color per tier: teal for LVL 1, amber for LVL 2, red for LVL 3. It's a CSS box-shadow pulse on a transparent overlay - no canvas involvement, no JS per frame.

Transition

On solve, outer hexes pull 30% inward toward the center node, then reverse outward past their original positions while fading out. The new board's hexes burst from center with an easeOutBack overshoot. The center hex stays fixed throughout.

High score

Stored in localStorage so it persists between sessions. Shown in the HUD and flagged as a new best on the game-over screen.