Documentation

Contestant Guide

Everything you need to know to submit a verified speedrun on ViberBlitz — from setting up your repo to passing every automated test.

How a run works

A ViberBlitz speedrun has five stages. Understanding each one helps you avoid surprises during submission.

1

Start the timer

Click Start on a challenge card. The clock begins the moment you click — this is your official start time. Build your app using any AI tool or combination of tools.

2

Build and push to GitHub

Build the required app, push it to a public GitHub repository. The repo must be public so the verifier can download it.

3

Submit

Click Finish, paste your GitHub repo URL, select the AI tool(s) you used, and optionally add a replay link. Submitting stops the clock.

4

Automated verification

ViberBlitz downloads your repo, installs dependencies, starts your app, and runs a Playwright test suite against it. This takes 30–90 seconds.

5

Result

If all tests pass, your time is recorded on the leaderboard. If any test fails, you see the full test output so you can fix and resubmit.

Repository requirements

The repo must be public. The verifier downloads it via the GitHub API. Private repos will fail immediately.

Plain HTML / CSS / JS (no build step)

If your app has no package.json, the verifier serves it as a static site automatically. No configuration needed — just make sure your entry point is index.html in the repo root.

my-calculator/
├── index.html   ← entry point (required)
├── style.css
└── script.js

Node.js apps (React, Vue, Next.js, etc.)

Your package.json must include a start script that launches the app. The verifier runs npm install then npm start.

{
  "scripts": {
    "start": "node server.js"
  }
}
Always use the $PORT environment variable. The verifier assigns a random available port and passes it via process.env.PORT. Hard-coding port 3000 will cause a timeout if that port is already in use.
// ✅ Correct — reads PORT from environment
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on ${port}`));

// ❌ Wrong — hard-coded port will fail if 3000 is taken
app.listen(3000);

Vite / React dev server

For Vite apps, use vite preview (not vite dev) for the start script, and pass the port:

{
  "scripts": {
    "build": "vite build",
    "start": "vite preview --port $PORT --host 0.0.0.0"
  }
}
Tip: If your AI tool generates a dev script but no start script, add one manually before pushing. The verifier looks for start first, then dev as a fallback.

How the verifier works

The ViberBlitz verifier is a server-side pipeline that runs automatically when you submit. Here is exactly what it does:

  1. 1.Downloads your repo as a ZIP archive from the GitHub API (no git binary required).
  2. 2.Extracts the ZIP and detects whether a package.json is present.
  3. 3.If no package.json: serves the directory as a static site using the serve package.
  4. 4.If package.json exists: runs npm install --legacy-peer-deps, then detects the correct start command (start → dev → vite preview → vite dev).
  5. 5.Starts the app on a randomly assigned port and waits up to 60 seconds for it to respond to HTTP requests.
  6. 6.Launches a headless Chromium browser via Playwright and navigates to the app.
  7. 7.Runs the challenge-specific test suite (see below for each challenge).
  8. 8.Returns pass/fail + full test output. On pass, records your elapsed time on the leaderboard.
  9. 9.Cleans up the temp directory.

Selector strategy

The verifier uses a multi-strategy approach to find elements in your app. For each element it needs to interact with, it tries these strategies in order:

1st

data-testid attribute

e.g. data-testid="btn-5" — always wins if present

2nd

ARIA role + name

e.g. role="button" with text "5"

3rd

Visible text content

any element whose visible text matches

Guaranteed PASS tip: Add data-testid attributes to your key elements (see per-challenge specs below). This bypasses all selector ambiguity and guarantees the verifier finds your elements regardless of DOM structure.

Challenge: Calculator (Beginner)

Build a functional calculator that supports addition, subtraction, multiplication, and division. The verifier runs 5 automated tests.

Test suite

Display renders — The app loads and shows a numeric display element.
Addition: 5 + 3 = 8 — Clicks 5, +, 3, = and checks the display shows 8.
Subtraction: 9 − 4 = 5 — Clicks 9, −, 4, = and checks the display shows 5.
Multiplication: 6 × 7 = 42 — Clicks 6, ×, 7, = and checks the display shows 42.
Division: 10 ÷ 2 = 5 — Clicks 1, 0, ÷, 2, = and checks the display shows 5.

Guaranteed PASS — recommended data-testid attributes

<!-- Display element -->
<div id="display" data-testid="display">0</div>

<!-- Number buttons -->
<button data-testid="btn-0">0</button>
<button data-testid="btn-1">1</button>
<button data-testid="btn-2">2</button>
<button data-testid="btn-3">3</button>
<button data-testid="btn-4">4</button>
<button data-testid="btn-5">5</button>
<button data-testid="btn-6">6</button>
<button data-testid="btn-7">7</button>
<button data-testid="btn-8">8</button>
<button data-testid="btn-9">9</button>

<!-- Operator buttons -->
<button data-testid="btn-+">+</button>
<button data-testid="btn--">−</button>
<button data-testid="btn-*">×</button>
<button data-testid="btn-/">÷</button>
<button data-testid="btn-=">=</button>

Challenge: Todo App (Intermediate)

Build a todo list app where users can add tasks, mark them as complete, and delete them. The verifier runs 5 automated tests.

Test suite

Renders a task input field — The app loads and shows an input field for entering new tasks.
Can add a new task — Types a task into the input and submits. Checks the task appears in the list.
Task appears in the list — Verifies the newly added task is visible in the task list.
Can mark a task as complete — Clicks the checkbox/complete button on the task. Checks it is visually marked as done (strikethrough, opacity change, or checked state).
Can delete a task — Clicks the delete button on the specific task. Checks it is removed from the list.

Guaranteed PASS — recommended data-testid attributes

<!-- Task input field -->
<input data-testid="todo-input" placeholder="Add a task..." />

<!-- Submit button (or press Enter in the input) -->
<button data-testid="todo-submit">Add</button>

<!-- Each task item in the list -->
<li data-testid="todo-item">
  <!-- Checkbox or complete button -->
  <input type="checkbox" data-testid="todo-complete" />
  <!-- Task text -->
  <span data-testid="todo-text">Buy groceries</span>
  <!-- Delete button -->
  <button data-testid="todo-delete">Delete</button>
</li>
Note on completion state: The verifier checks that the task is visually marked as done after clicking complete. Accepted patterns include: text-decoration: line-through, an opacity change, a CSS class like completed, or the checkbox being checked. Any visible change works.

Challenge: PDF Analyzer (Advanced)

Build an app that accepts a PDF file upload and extracts or displays its text content. The verifier runs 5 automated tests.

Test suite

App renders a file upload input — The app loads and shows a visible file input element.
Accepts PDF file type — The file input has accept=".pdf" or accept="application/pdf". The input must be visible (not hidden with opacity:0 or display:none).
Upload button or drag area is visible — A visible upload button or drag-and-drop zone is present on the page.
Page has a result or output area — An output area for displaying extracted text is visible on the page. It must be in the DOM and visible at load time — do not hide it with display:none until after upload.
Page title or heading mentions PDF or analyzer — The page <title> or a visible heading contains the word "PDF" or "analyzer".

Guaranteed PASS — recommended data-testid attributes

<!-- File upload input (must be visible — do NOT use opacity:0 or display:none) -->
<input
  type="file"
  accept=".pdf,application/pdf"
  data-testid="file-input"
/>

<!-- Drag-and-drop upload zone (optional but recommended) -->
<div data-testid="upload-area">
  Drop PDF here or click to browse
</div>

<!-- Output area (must be visible at page load — use placeholder text) -->
<div data-testid="extracted-text">
  Extracted text will appear here...
</div>
Front-end only is fine. You can use PDF.js (loaded from CDN or bundled locally) to extract text entirely in the browser — no backend required. If you use a CDN, note that the verifier sandbox has no internet access, so bundle the library locally in a libs/ folder and reference it with a relative path.

Common pitfalls

Hard-coded port number

The verifier assigns a random available port. If you hard-code 3000 and that port is taken, your app will fail to start.

✗ Avoidapp.listen(3000)
✓ Useapp.listen(process.env.PORT || 3000)
Missing start script

The verifier looks for a start script. If only dev is present it will try it as a fallback, but start is more reliable.

✗ Avoid"scripts": { "dev": "vite" }
✓ Use"scripts": { "dev": "vite", "start": "vite preview --port $PORT --host 0.0.0.0" }
Private GitHub repo

The verifier downloads your repo via the GitHub API without authentication. Private repos return a 404.

✗ AvoidRepo visibility: Private
✓ UseRepo visibility: Public
No index.html in root (static sites)

For plain HTML apps with no package.json, the verifier serves the repo root. Your index.html must be at the root, not inside a subdirectory.

✗ Avoidsrc/index.html
✓ Useindex.html (at repo root)
Build output not committed

If your app requires a build step, either commit the dist/ folder or include the build command in your start script.

✗ Avoiddist/ in .gitignore, no build step in start script
✓ Use"start": "npm run build && vite preview --port $PORT"
Buttons that are not real buttons

The verifier tries to click buttons by role and text. Div-based buttons without role="button" may not be found. Use real <button> elements or add data-testid attributes.

✗ Avoid<div class="btn" onclick="...">5</div>
✓ Use<button data-testid="btn-5">5</button>

FAQ

Ready to run?

Pick a challenge, build fast, and get verified. Any AI stack. Permanent record.

View Challenges