Matterhorn 03-003 · PDF/UA-1 UA1:7.1-5

JavaScript that makes a PDF flash: finding the timer

Human decisionCheckpoint 03: FlickeringWCAG 2.3.1 (A)

Document-level or field-level JavaScript animates content at a rate that can trigger seizures.

The condition, as the protocol states it. “One or more JavaScript actions lead to flickering.”

Matterhorn Protocol 1.1, checkpoint 03, index 03-003, section UA1:7.1-5. The protocol marks this as requiring human judgement.

What a screen reader actually does

Not a screen-reader issue — a seizure-risk issue for sighted readers.

How a PDF script makes something flash

Acrobat’s JavaScript can change what is on the page after it has been drawn: hide and show a field, change its fill or text colour, switch a layer on and off, move to another page. On its own each of those happens once. What turns them into flashing is a timer:

// Page Open script on page 1
app.setInterval(
  'var f = this.getField("banner");' +
  'f.display = (f.display == display.hidden) ? display.visible : display.hidden;',
  100                       // milliseconds
);

That is a field flipping ten times a second, which is five flashes a second. app.setInterval repeats an expression every so many milliseconds until it is cleared; app.setTimeOut runs it once after a delay, and a chain of time-outs can do the same thing.

The other common source is frame animation. LaTeX’s animate package builds an animation from a sequence of frames and plays it with JavaScript at the frame rate the author chooses. Twelve frames a second alternating between a dark and a light frame is six flashes a second, and nothing in the document will tell you so until you watch it.

Where scripts live

Scripts are actions of type /JavaScript, so they can hang off anything that has a trigger:

% the document's own scripts: a name tree off the catalog
/Names << /JavaScript << /Names [ (init) 51 0 R ] >> >>
/OpenAction << /S /JavaScript /JS (…) >>          % when the document opens

% a page
/AA << /O << /S /JavaScript /JS (…) >>             % Page Open
       /C << /S /JavaScript /JS (…) >> >>          % Page Close

% a form field
/AA << /K … /F … /V … /C … >>                      % keystroke, format, validate, calculate

Annotations and fields also have triggers for the pointer entering and leaving, the mouse button and focus. A script anywhere in that list can start a timer that runs for as long as the document is open, which is why reviewing only the obvious places misses it.

What happens to the reader

A sighted reader with photosensitive epilepsy is at risk of a seizure; a reader using magnification has the flashing area enlarged. A screen-reader user is not the one at risk here, which is part of why it gets missed: a review done by listening finds nothing.

Scripts only run in viewers that support them. In a browser’s built-in viewer, a phone’s, or a desktop viewer without Acrobat’s JavaScript engine, the page may sit still — so a document can pass a reviewer who used one viewer and fail a reader who uses another.

How to check it free

List the scripts. Poppler’s pdfinfo, free for Linux, macOS and Windows, prints the scripts attached to the document, its pages, its fields and its annotations, each with the trigger it hangs on:

$ pdfinfo -js report.pdf
Name Dictionary "init":
app.beep(0);

Page Open:
app.setInterval('var f = this.getField("banner"); …', 100);

Field Modified:
…

Labels include Open Document Action, Page Open, Page Close, Field Modified (keystroke), Format Field, Validate Field, Calculate Field and the widget triggers such as Widget Annotation Cursor Enter. Search the output for setInterval, setTimeOut, display, fillColor, textColor and getOCGs (layers), and read anything that combines a timer with a visual change. Tested on a generated file, it missed two places: a script on a bookmark, and a script chained to run after another action. A bookmark’s actions are on the Actions tab of its Properties in the Bookmarks panel (Adobe: Add actions to PDF elements).

In Acrobat Pro, Adobe documents three places for the same review: All tools → Use JavaScript → All javascript strings opens the JavaScript Editor; Document javascript strings is where Adobe says to add, edit or delete the document’s scripts; and Document actions holds the scripts tied to document events (Adobe: Add debug JavaScript).

Then watch it, in Acrobat or Reader, on every page that has a script, for long enough that a timer would have fired. Anything that blinks, count; anything close to three flashes a second, record and measure with the Trace Center’s free PEAT.

Acrobat’s Accessibility Checker carries two rules that point here, Screen flicker and Scripts. Both are prompts to do this review — Adobe’s guidance for each is to remove or modify the script, not a test of one (Adobe: Create and verify PDF accessibility).

How to fix it free

Slow it, dim it, or stop it. Raising the interval above the threshold, changing between two colours of similar brightness, or shrinking what changes are all legitimate; so is removing the timer and showing the end state. Then measure again.

Edit the script where it lives. For document-level scripts, Document javascript strings lets you edit or delete them. For a form field or a button, Adobe documents the field’s Properties → Actions tab, where each trigger’s actions are listed with Edit and Delete (Adobe: Form fields general settings); for a page, the Actions tab of Page properties in the Page thumbnails panel (Adobe: Add actions to page thumbnails).

For a LaTeX animation, fix it in the source (LaTeX to accessible PDF covers the rest of that toolchain): lower the frame rate, remove the alternating frames, or replace the animation with a still figure and a sentence. Rebuilding is cheaper than editing the generated scripts.

What Taggart does

Taggart does not read JavaScript. It has no check for this condition and no list of the scripts in a document, but its repairs remove every script that runs on its own — document-level scripts, and those set to run when the file or a page opens — so a Page Open timer is gone after a repair. A script behind a form field or a button runs only when the reader acts, and Taggart leaves it as it was: if it flashes, it still flashes.

What it will not do is call the file conformant on that basis. On a test file whose problems were all of this kind — a Page Open timer toggling a field every 100 milliseconds among them — Taggart found nothing, and its conformance statement stopped at machine checks passed, listing 03-003 among the conditions Taggart does not check that no person had yet confirmed. Asked to declare conformance, it left the PDF/UA identifier out. It writes it only after someone confirms the whole list — the checkbox on the application’s conformance step — and from then on it takes that confirmation as given.

So the confirmation is where this page’s method belongs. Run pdfinfo -js, read what it prints, watch what needs watching, and only then tick the box. Taggart’s report records that the conditions were confirmed, not how; keep the pdfinfo output with the file.

How Taggart handles it

No software can decide this one, and Taggart has no check for it, so it never appears among the findings or in the review queue. It is on the list of conditions Taggart asks you to confirm by hand before it writes the PDF/UA claim into the file — the claim waits until you have.

Whatever Taggart changes, the page still looks identical. Fixes edit the PDF object model, not the content streams, and every remediation is re-rendered and compared against the original at structural similarity ≥ 0.999 before you get the file back. A tool that quietly reflows your document is worse than no tool.

Frequently asked questions

How fast is too fast?

Count changes, then halve. WCAG counts a flash as a pair of opposing changes, so a script that toggles something every 100 ms makes five flashes a second, and the limit is three. Anything toggling faster than about every 167 ms is over, unless the area is small or the change is too faint to cross the general and red flash thresholds.

I opened the file and nothing moved. Am I done?

Only if you opened it in a viewer that runs the scripts. Many PDF viewers run no JavaScript, or only part of Acrobat's, and in those the page sits still. Test in Acrobat or Acrobat Reader, which is where the author's scripts were written to run.

Is all JavaScript in a PDF an accessibility problem?

No. Most of it formats numbers, adds up totals and checks dates, and is harmless. It becomes a problem when it does something only some readers can perceive or keep up with: flashing (this condition), signalling only with sound (05-003), or depending on typing speed (29-001).

Does a slower animation pass?

Below three flashes a second, yes, as far as 2.3.1 goes. An animation that starts by itself, runs for more than five seconds and sits alongside other content also needs a way to pause, stop or hide it under WCAG 2.2.2, which is a separate requirement.

Where this sits in the standards

Standards this condition maps to
StandardReference
Matterhorn Protocol 1.1Checkpoint 03 (Flickering), index 03-003
PDF/UA-1 (ISO 14289-1)Clause 7.1-5
WCAG 2.12.3.1 Three Flashes or Below Threshold — Level A
EN 301 549 / Section 508 / ADA Title IIAll three point at WCAG 2.1 Level AA for non-web documents, so a Level A or AA criterion here is in scope for each of them.

Other conditions in checkpoint 03

Checkpoint 03 covers flickering. These are its other failure conditions; the ones without a link are in the rule set but do not have a written page yet.

Check your own file. Taggart’s validator is free and unlimited — 75 of the 87 Matterhorn conditions software can decide, with the other 12 listed for you to confirm; no watermark, no expiry, and no account needed up to 60 pages.

Check a PDF free

This condition needs a person’s judgement, so there was nothing for a tool to check. Written from ISO 14289-1 and the Matterhorn Protocol on . The Acrobat steps follow Adobe’s documentation and were not walked through by hand. Last updated .