Accessible gaming
Can You Beat DOOM With One Button?
I built a real one-switch ViZDoom controller and ran 36 combat and maze trials to measure when auto-scanning works, and when the clock wins instead.
Summarize this article
Open a prefilled request in your preferred assistant.

TL;DR
Yes, inside the tested boundary: a controller with one binary select now input completed both a combat task and a maze-navigation task in the ViZDoom engine.
The combat task succeeded in all 18 trials. The maze succeeded in 6 of 6 trials at a 114 ms scan dwell, 6 of 6 at 257 ms, and 3 of 6 at 514 ms; the three failures hit the scenario's 60-second timeout.
The reproducible artifact includes the one-switch scanner, an interactive player, 36 raw trials, CSV output, engine frames, and every recorded switch selection. An automated observer chose which action it wanted, so this proves that the control channel can express a winning sequence; it does not prove that the interface is comfortable or usable for any particular person.
The banana beat the monster. The snail beat the banana.
One button is a timing channel, not one command
A single-switch interface does not have to map one button to one permanent action. Instead, the software highlights possible actions in sequence, and the user activates the switch when the desired option is highlighted. The same physical event can therefore mean forward, turn left, or attack; timing supplies the missing vocabulary.
This is ordinary auto-scanning, not a loophole invented because I wanted a banana to play Doom. Apple's current Switch Control documentation describes the same basic interaction: with one switch, the interface automatically scans items and the switch selects the highlighted item. It also exposes scanning speed, repeat filtering, hold duration, and menu ordering because those timing details determine whether a theoretically complete interface is practical.

The controller resets to the first item after every selection, then cycles through exactly three actions. Doing nothing advances the scanner and the Doom engine together. Pressing selects the highlighted action and runs a short macro measured in engine tics.
A press does not mean “move forward forever”; it means “hold the forward button for eight Doom tics,” after which scanning resumes. ViZDoom uses the Doom engine's default rate of 35 tics per second, so that macro lasts about 229 ms according to the official time-conversion utilities.
What beat DOOM means in this experiment
I did not complete a commercial Doom campaign with one button, and the artifact does not quietly ship proprietary game data. I used ViZDoom 1.3.0, an open research interface built on the ZDoom engine, plus two scenarios distributed with its Python package. ViZDoom exposes the rendered screen, engine actions, game variables, objects, and map geometry, which makes every run reproducible instead of relying on a video and my solemn promise that the button was connected.
| Scenario | Goal | Scanner menu | Success condition |
|---|---|---|---|
basic | Aim at and kill one Cacodemon | Attack, strafe left, strafe right | Engine kill count reaches one |
my_way_home | Navigate a small maze and collect green armor | Forward, turn left, turn right | Engine armor value becomes positive |
The combat task is deliberately small. The target spawns at different horizontal positions, so the controller must align the weapon and fire, but it is not a substitute for a complete level containing doors, weapon changes, elevators, secrets, and somebody's 1994 understanding of indoor architecture.
The maze is the stronger task. The player starts in one of several locations and must cross a graph of connected rooms before the scenario's default 2,100-tic timeout, which is 60 seconds at the standard tic rate. There are no monsters in that scenario; the clock is sufficient wildlife.
The one-switch controller
The experiment has one selection event. In the interactive player, either SPACE or one mouse-button click emits that event, so an adaptive switch configured to produce a Space key can use the same path. ESC can close the test harness, but it is never sent into the game and does not help complete a scenario.
| Menu | Highlighted action | Engine button | Duration |
|---|---|---|---|
| Combat | Attack | ATTACK | 4 tics |
| Combat | Strafe left | MOVE_LEFT | 3 tics |
| Combat | Strafe right | MOVE_RIGHT | 3 tics |
| Maze | Forward | MOVE_FORWARD | 8 tics |
| Maze | Turn left | TURN_LEFT | 2 tics |
| Maze | Turn right | TURN_RIGHT | 2 tics |
A scanner highlight has only two outcomes: no press sends a neutral action while the engine advances, and one press sends the highlighted macro. The policy cannot call MOVE_FORWARD, ATTACK, or any other ViZDoom action directly. It returns a name, then waits for the scanner to offer that name.
This makes the channel restriction testable. A clever policy can know exactly where it wants to go and still lose because it has to sit through turn left and turn right before forward appears again. Intelligence does not waive the queue.
The observer was automated on purpose
A human can run the interactive player and use the visible scanner, but the recorded 36-trial matrix uses an automated observer. That choice isolates the controller: the experiment asks whether the one-switch channel can execute a successful sequence under different scan delays, without mixing in practice, fatigue, switch placement, visual tracking, or an author's suspiciously convenient gaming skill.
For combat, the observer reads the target's horizontal location from ViZDoom's label buffer. It requests strafe left or right until the target overlaps a center band, then requests attack.
For the maze, the observer reads the player's coordinates, the armor coordinates, and the map's sector boundaries. It matches nonblocking boundaries shared by two sectors, builds a portal graph, finds a shortest sector path with breadth-first search, and converts each portal midpoint into a waypoint. At every scanner highlight, it compares the player's angle with the next waypoint and requests left, right, or forward.
The observer therefore has privileged engine state. It is not a screen-only agent, and it is absolutely not a simulated disabled person. Its job is narrower: produce a consistent desired action so any failures can be attributed to the scanner, action macros, movement controller, or scenario timeout.
The 36-trial protocol
I ran six deterministic seeds for each combination of scenario and scan dwell. The three dwell values were 4, 9, and 18 engine tics, which correspond to approximately 114, 257, and 514 ms per highlighted action at 35 tics per second.
The resulting matrix contained 36 trials: two scenarios, three scan dwell conditions, and six seeds per condition.
Each trial recorded the menu order, requested action macros, switch activations, scanner highlights, scan-wait tics, controller-scheduled engine tics, reward, health, armor, kill count, final distance to the maze goal, and terminal reason. The environment record includes Python, Windows, and ViZDoom versions; the complete JSON has a published SHA-256 checksum.
The engine runs synchronously and headlessly, so wall-clock execution speed is irrelevant. “Elapsed seconds” below means scheduled Doom engine time, capped by the scenario timeout, not how long Python occupied the laptop.
One button won 33 of 36 runs
| Scenario | Scan dwell | Wins | Median presses on successful runs | Median engine time on successful runs |
|---|---|---|---|---|
| Combat | 114 ms | 6 / 6 | 3 | 0.429 s |
| Combat | 257 ms | 6 / 6 | 3 | 0.571 s |
| Combat | 514 ms | 6 / 6 | 3 | 0.829 s |
| Maze | 114 ms | 6 / 6 | 62 | 17.143 s |
| Maze | 257 ms | 6 / 6 | 66 | 33.900 s |
| Maze | 514 ms | 3 / 6 | 46 | 41.714 s |
The combat result is uncomplicated: all 18 targets died. Because attack is the first menu item and most spawns need only a small alignment correction, the median successful run required three presses. Increasing the scanner dwell still increased engine time, but the task had enough slack that none of the combat trials approached its timeout.
The maze exposed the actual tradeoff. At 114 and 257 ms, all twelve runs reached the armor. At 514 ms, only three of six did. The median of 46 presses in that final row describes the successful subset; it is lower than the faster conditions because the three successful seeds began on a shorter route, not because a slower scanner discovered efficiency through personal growth.

The slow scanner lost to the clock
All three failed runs occurred in the 514 ms maze condition, and every failure ended at the scenario timeout rather than death or an invalid action. Their final distances from the armor were approximately 314, 399, and 409 map units.
The scanner did not lose expressive power at 514 ms. It still offered forward, left, and right in the same order, and the policy still requested valid routes. The interface failed because selection latency consumed the finite episode. A complete action vocabulary is necessary, but it is not enough when the environment continues moving while the user waits.

The experiment also explains why action ordering cannot be treated as decoration. Forward appears first in the maze menu because it is the most frequent request along a valid path; after a press, scanning resets there. Putting a rare action first would add neutral tics to most selections.
Research on single-switch selection has explored exactly this throughput problem. Nomon, for example, uses the timing of repeated clicks and a probabilistic model instead of a fixed tree, and its authors compared the method with row-column scanning rather than assuming that all one-switch interfaces behave alike. Counting buttons tells you the channel width; it does not tell you the usable information rate.
This is an engine experiment, not a disability simulation
The automated observer is useful because it never gets tired, misses the switch, loses visual focus, or changes posture. Those are also the reasons it cannot answer the human question.
Real switch access depends on activation reliability, body site, mounting, dwell time, recovery delay, repeated-trigger filtering, auditory feedback, visual highlight size, and individual preferences. A 2019 evaluation of Scanning Wizard worked with switch users and practitioners, then measured how individualized settings affected text entry over time. That kind of participant work is evidence about usability. My bot reaching green armor is evidence that three timed choices can steer a Doom player through a maze.
The distinction is not ceremonial. If I called the automated runs an accessibility success, the clean 18-of-18 combat result would hide every physical and cognitive demand that the policy bypassed. The artifact instead provides an interactive player and an instrumentation path that a properly designed human study could use later, with consent, individualized timing, error logging, and fatigue measures.
For now, the claim stops at the controller boundary.
Reproduce or play it
The published experiment README contains the setup commands and limitations. The important files are small enough to inspect without rerunning anything:
run_experiment.pycontains the scanner, observers, path planner, trial loop, and evidence builder.play_one_switch.pyexposes the same scanner through Space or one mouse-button click.results.jsoncontains the aggregate table and every trial.trials.csvis the flat measurement table.switch-events.jsonrecords a complete successful slow-scan selection sequence.
python -m venv .venv
..venvScriptspython.exe -m pip install -r requirements.txt
..venvScriptspython.exe run_experiment.py
..venvScriptspython.exe verify_results.py
The quickest falsification is simple: install the pinned dependencies and rerun the matrix. The verifier checks all 36 trials, the 33-to-3 win-loss split, the failure class, and the result checksum rather than politely converting a changed output into prose.
Sources and artifacts
- Complete reproducible one-switch ViZDoom experiment
- Machine-readable 36-trial result set
- ViZDoom repository and bundled scenarios
- ViZDoom action and episode model
- Apple Switch Control auto-scanning documentation
- Fast and Flexible Selection with a Single Switch
Conclusion
One button beat both tested Doom-engine tasks because auto-scanning converted press timing into a small action vocabulary, and short macros converted each selection into enough movement to matter. Across 36 trials, the controller won 33 times; the only failures appeared when the slowest scanner spent the maze's 60-second allowance highlighting actions that were not currently needed.
That answers the engineering question I actually tested: the binary channel was sufficient, but its timing policy decided whether sufficiency arrived before the clock. It does not answer whether a particular switch user would enjoy, tolerate, or reliably operate this interface. That needs participant work, individualized settings, and considerably less banana-based instrumentation.