Skip to content
PDFGame

Under the hood · 9 min read · Updated

How PDF Games Work: JavaScript, Form Fields and a Lot of Nerve

A PDF can carry JavaScript, and form fields can be shown, hidden and rewritten by that script. Put the two together and you have a tiny, very strange game console.

Most people think of a PDF as digital paper: fixed text, fixed images, a format whose whole point is that it looks the same everywhere. That's true of the page content. But the PDF specification also defines interactive forms, annotations and actions — including actions written in JavaScript. Those features exist so tax forms can add up totals and validate dates. Game developers noticed they're also enough to build Tetris.

This guide walks through the building blocks every PDF game on this site uses, with real object syntax, and explains why the same file works in Chrome but does nothing on your phone.

JavaScript has been in PDF for decades

Adobe added JavaScript to Acrobat in the late 1990s to power smarter forms, and JavaScript actions became part of the PDF specification around PDF 1.3. The language is ordinary JavaScript; what's special is the API around it. Instead of the browser's window and document, a PDF script gets objects such as app (the viewer application), this (the current document), event (whatever just happened) and util (formatting helpers).

Scripts can be attached in several places:

  • Document-level scripts, stored in the catalog's /Names /JavaScript tree, run once when the file opens and usually define shared functions.
  • The catalog's /OpenAction, which runs when the document is opened.
  • Page actions (/AA with /O for page open and /C for page close).
  • Field and widget actions: /K (keystroke), /F (format), /V (validate) and /C (calculate) on fields, plus mouse events such as /D (mouse down), /U (mouse up), /E (enter), /X (exit), /Fo (focus) and /Bl (blur) on widget annotations.
  • A push button's /A action, which fires when it is clicked.

Here is a complete push button that shows an alert when clicked. It is a widget annotation (so it has a position on the page) and a form field (so scripts can find it by name):

pdf
8 0 obj
<< /Type /Annot /Subtype /Widget
   /FT /Btn /Ff 65536            % Ff bit 17 = push button
   /T (hello)                    % field name for this.getField("hello")
   /Rect [72 600 192 630]        % position in points, origin bottom-left
   /MK << /CA (Click me) >>      % caption
   /A << /S /JavaScript
         /JS (app.alert\("Hello from inside a PDF"\);) >>
>>
endobj
A push-button field with a JavaScript action

Form fields are the screen

There is no canvas in the PDF JavaScript API. You can't draw a line or set a pixel. What you can do is change properties of form fields: their value (text), their visibility via field.display or field.hidden, their fill and border colours, and in some viewers their position via field.rect.

So PDF games build a display out of fields. PDFtris places one small field per cell of the Tetris well and toggles each cell between visible and hidden every frame. Breakout does the same for bricks, ball and paddle. DoomPDF goes the other way: instead of thousands of tiny fields it uses one text field per row of the screen and writes a line of ASCII characters into each, so a frame is a stack of strings.

js
function draw(grid) {
  for (var y = 0; y < ROWS; y++) {
    for (var x = 0; x < COLS; x++) {
      var cell = this.getField("c_" + x + "_" + y);
      cell.display = grid[y][x] ? display.visible : display.hidden;
    }
  }
}
The core of a field-based renderer

Every property change asks the viewer to regenerate that widget's appearance, which is expensive. That is the main performance limit for PDF games, and why good ones only touch fields that actually changed since the last frame.

The game loop: app.setInterval

Games need time to pass on its own. The PDF API provides app.setInterval(expression, milliseconds) and app.setTimeOut(expression, milliseconds), with app.clearInterval and app.clearTimeOut to cancel them. One quirk: the first argument is a string of code to evaluate, not a function. Several games on this site wrap it so they can pass functions instead:

js
function setInterval(cb, ms) {
  var evalStr = "(" + cb.toString() + ")();";
  return app.setInterval(evalStr, ms);
}
The wrapper used by Checkers PDF and Flappy Bird Classic

Flappy Bird PDF asks for a 10 ms tick and Breakout for 15 ms. In practice the viewer runs the callback as often as it can, and heavy redraws slow it down — so physics should use elapsed time (Date.now()) rather than assume each tick is exactly the interval you asked for.

Input: keystroke events and button presses

A PDF script can't attach a global keyboard listener. It only hears about keys typed into a focused text field. When that happens the field's /K keystroke action runs, and event.change contains the character just typed. PDFtris's entire input handler is a switch on that value:

js
function handle_input(event) {
  switch (event.change) {
    case 'w': rotate_piece(); break;
    case 'a': move_left(); break;
    case 'd': move_right(); break;
    case 's': lower_piece(); break;
  }
}
Input handling in PDFtris (abridged)

This is why most PDF games ask you to click an input box first and why arrow keys rarely work — they don't produce characters. Setting event.rc = false or clearing the field afterwards keeps the box from filling up; DoomPDF simply resets its input field once a second.

Mouse input uses widget actions. A push button's mouse-up action is a click (Flappy Bird PDF makes the entire page one giant button). Mouse-down and mouse-up separately give you press-and-hold, which DoomPDF uses for its on-screen arrows. And mouse-enter can even fake cursor tracking: Breakout tiles the playfield with invisible one-point-wide fields and records whichever one the pointer entered last.

Which viewers actually run PDF JavaScript

The PDF spec describes the API, but each viewer decides how much of it to implement. That's the single biggest factor in whether a PDF game works for you.

  • PDFium — the engine in Chrome, Edge, Brave and other Chromium browsers. Google open-sourced it in 2014. It implements a practical subset of the Acrobat API, including timers, field properties and keystroke events, which is why almost every PDF game targets it.
  • PDF.js — Firefox's viewer, written in JavaScript. Around 2021 it gained scripting support that runs document scripts in a separate sandbox. Coverage is narrower and performance different, so some games run and others don't.
  • Adobe Acrobat and Acrobat Reader — the reference implementation with the widest API. Ironically, many browser-targeted PDF games are never tested there and can behave differently.
  • Apple Preview, Safari, iOS and most Android viewers — render the page but ignore JavaScript. Games load as a static picture.

Is it safe?

PDF JavaScript has a security history. In the late 2000s, bugs in Adobe Reader's JavaScript functions were a favourite way to deliver malware through email attachments, and Adobe responded with sandboxing (Protected Mode) and options to disable scripting. In 2024, Thomas Rinsma — the author of PDFtris — disclosed a PDF.js bug (CVE-2024-4367) that allowed arbitrary JavaScript to run in the context of the viewer; it was patched quickly.

In modern browsers, PDF scripts run inside the browser's own sandbox with a deliberately limited API: no file system, no arbitrary network access, no access to other tabs. Games like those on PDFGame use nothing beyond timers and form fields. The sensible rule is the same as for any file: keep your browser updated and only open PDFs from sources you trust.

The limits — and why people do it anyway

PDF games are slow, low-resolution and fussy about viewers. There's no sound, no real graphics API and no reliable way to read the keyboard. That's the appeal. Like running Doom on a calculator, it's a puzzle about constraints: what's the least a platform needs to host a game? The answer, apparently, is a timer, a text box and a lot of checkboxes.

FAQ

Q1Can a PDF really contain a game?

Yes. PDFs can include JavaScript that runs when you interact with form fields or open the document. Games use that script to move form fields around like pixels.

Q2Which browser is best for PDF games?

A desktop Chromium browser — Chrome, Edge or Brave — because their PDFium engine supports the timers and field events games rely on.

Q3Why don't PDF games work on my phone?

Mobile PDF viewers render pages but don't run PDF JavaScript, so nothing can move.

Q4Can a PDF game access my files?

No. Browser PDF viewers run scripts in a sandbox with no file-system or general network access.

Keep reading