← All projects
01 Web platform · 2025-present

critical.lol

A personal web project where I publish browser games and Windows DFIR research. The work ranges from small JavaScript experiments to local machine learning, a Three.js game, and a real-time multiplayer system.

Current project surface
critical.lol one public domain
PLAY7 browser gamesDOM, Canvas, WebGL
READDFIR blogMarkdown-backed articles
SHARENode serviceREST API + Socket.IO
Each part is independently useful; the shared service only appears where it earns its complexity.
7playable games
9REST routes
6published posts
13K+article words
532party-game cards

The project grew by following individual ideas, not by forcing one stack onto everything.

critical.lol began as a place to ship experiments. Some games only need HTML, CSS, and browser state. Others need a shared question set, local inference, a 3D scene, or synchronized rooms. The architecture stays intentionally uneven: each experience gets the smallest system that can support it.

My role
Design, front end, game logic, API, content, deployment
Main tools
JavaScript, Node.js, Express, Socket.IO, Three.js, ONNX Runtime
Content model
JSON datasets and Markdown files kept in the repository
Design goal
Make technically different ideas feel like parts of the same site

A static-first site with one shared service.

The browser owns presentation and most game logic. Express supplies shared datasets and blog content; Socket.IO is reserved for the multiplayer game.

How the main parts connect
BROWSER
Home + directorydiscovery and navigation
Game clientsDOM, Canvas, WebGL
Blog readerfiltering and Markdown rendering
NODE SERVICE
Express APIquestions, posts, cards, suggestions
Socket.IOrooms, heartbeats, versioned writes
SOURCE DATA
JSON90 Big-O questions, 278 quotes
Card sets278 family + 254 adult prompts
Markdownsix long-form blog posts
01

Keep the clients direct.

Most interactions happen in the browser. The API is not inserted between the player and every click.

02

Keep content inspectable.

Questions, cards, and articles remain ordinary files rather than opaque records in a custom CMS.

03

Add state only when shared.

Patent Pending uses server rooms because several phones and one TV must agree on the same round.

Seven games, each built around a different technical question.

The complete directory has direct links and longer summaries. This is the engineering view.

Open games directory

Three places where the implementation changes the experience.

The excerpts below are shortened from the project source, but preserve the real control flow.

04.1 / SHARED CONTENT

The Big-O game asks the server for a fresh question.

The route samples the JSON dataset and builds a new answer order. By default it withholds the solution; adding ?reveal=1 includes the answer, hint, and explanation.

Why the API?
The dataset can change without editing the client.
What stays client-side?
Round flow, scoring, hints, and the game interface.
node/index.jsGET /api/v1/big-o/get_question
app.get('/api/v1/big-o/get_question', (req, res) => {
  const reveal = String(req.query.reveal || '').trim() === '1';
  const q = pick(bigOData.questions);
  const payload = {
    id: nanoid(10),
    code: q.code,
    choices: buildChoices(q.answer, bigOData.allComplexities)
  };

  if (reveal) {
    payload.answer = q.answer;
    payload.hint = q.hint;
    payload.explanation = q.explanation;
  }

  res.json(payload);
});
04.2 / LOCAL INFERENCE

Double Doodle classifies the canvas without uploading it.

The game resizes the drawing to 64 by 64 pixels, converts it into a normalized tensor, and runs the ONNX model through WebGL with WASM as a fallback. Predictions update during the round.

Canvas64 x 64 tensorONNX sessionTop labels
game/double-doodle/script.jsIN-BROWSER MODEL
const tensor = preprocessCanvasToTensor();
const feeds = {};
feeds[inputName] = tensor;

const results = await ortSession.run(feeds);
const logits = results[outputName].data;
const probs = softmax(logits);
updateProbsUI(probs);

ortSession = await ort.InferenceSession.create(modelData, {
  executionProviders: ['webgl', 'wasm']
});
04.3 / SYSTEMIC HORROR

Someone's Knocking turns hidden sanity into encounter probability.

The game never displays a sanity meter. Instead, lower stability changes audio, visuals, timing, and the likelihood that a visitor is deceptive. The player reads the system through the apartment rather than through a status bar.

Runtime
Three.js scene, pointer-lock movement, collisions, tasks, and a peephole render target.
Audio
Knocks and ambience are synthesized with the Web Audio API.
game/someone-is-knocking/index.htmlSCENARIO SELECTION
function pickScenario(){
  const t = GAME.timeSec;
  const s = psyche.sanity;

  const prog = clamp(t / 360, 0, 1);
  const fakeChance = clamp(
    0.18 + (1 - s) * 0.45 + prog * 0.22,
    0.18,
    0.78
  );

  const isFake = Math.random() < fakeChance;
  const pool = isFake ? FAKE_ENTITIES : REAL_ENTITIES;
  return { sc: pool[Math.floor(Math.random() * pool.length)], isFake };
}

Patent Pending separates the shared display from player controls.

The TV creates the room and directs timing. Phones join with an eight-digit code and render only the action needed for the current phase.

Open the game
One round, simplified
HOSTTVcreates room, advances phases, plays narration
SOCKET.IORoom 48273105versioned state + host heartbeat
Phone Asubmit / pitch
Phone Binvest / score
Phone Cinvest / score
  1. 01Lobbyjoin room
  2. 02Promptreceive constraints
  3. 03Submitinvent company
  4. 04Pitchpresent idea
  5. 05Investallocate funds
  6. 06Scoreupdate totals
01

Heartbeat cleanup

The host updates hostBeat. Rooms that stop receiving it are removed from in-memory storage.

02

Optimistic versions

Every write includes the version it started from. A stale client receives a conflict instead of overwriting newer state.

03

Speech gates

Intro phases wait for narration to finish before starting the timer, keeping the shared screen understandable.

node/index.jsCONFLICT CHECK
const baseV = Number(payload?.baseV || 0);
const curV = Number(cur.v || 0);

if (baseV && baseV !== curV) {
  return ack && ack({
    ok: false,
    error: 'conflict',
    room: ppPublicRoom(cur)
  });
}

merged.hostBeat = cur.hostBeat;
merged.v = curV + 1;

Research stays portable from source file to offline copy.

The blog is part of the same project, but its system is deliberately simple: Markdown on disk, cached JSON from the API, and a browser reader.

Read the blog
Publishing path
01 / WRITEMarkdown + front matterportable source in the repository
02 / LOADRecursive Node parsersorts posts and computes validators
03 / SERVEJSON APIETag, Last-Modified, short cache
04 / READBrowser + offline ZIPfilter online or download the collection
WINDOWS DFIR

Task Scheduler Bypass Detection

Following persistence through scheduler files, registry data, memory, and USN records.

MEMORY FORENSICS

Navigating Memory Like A File System

Using MemProcFS to explore processes, registry evidence, devices, and network traces.

WINDOWS INTERNALS

The Fun Limitations of Prefetch

Connecting SysMain and execution evidence while examining where the artifact breaks down.

NODE.JS + DFIR

Live Processes are Hell For Finding Things

Automating string searches across live process memory with a read-only native module.

The value of the project is the range, but range needs boundaries.

Building many small products under one domain made the tradeoffs unusually visible.

01

Do not centralize by default.

A shared service is useful for common data and multiplayer state. It would only slow down the self-contained games.

02

Browser capability is a design material.

WebGL, Web Audio, Canvas, and WASM are not just implementation details; they determine which ideas are possible without installation.

03

Content deserves an exit path.

Keeping articles in Markdown and supporting an offline bundle means the writing outlives the current reader interface.

04

Experiments still need operational rules.

Rate limits, room expiry, version checks, input limits, and cache validators keep playful software from becoming careless software.

PROJECT SUMMARY

A home for finished games, unfinished questions, and technical writing.

critical.lol is not one application pretending to be a platform. It is a growing set of browser projects with a small amount of shared infrastructure where sharing actually helps.

Next projectVise Core