Skip to main content
stable-retro-turbo

🚀 Blazing-fast Stable Retro fork with native vectorization and preprocessing 🚀

stable-retro-turbo is a Python library for reinforcement-learning developers who need faster batched rollouts from classic console games. It keeps Stable Retro's game integrations and single-environment API, and adds RetroVecEnv, a Gymnasium vector environment that steps many libretro emulators and preprocesses observations in native code.

Install the package, import your legally obtained ROMs, and use the upstream-compatible stable_retro import. The native path is useful for parallel training workloads that would otherwise spend substantial time crossing between Python wrappers and individual emulator instances.

Install

Release wheels require Python 3.14 and support macOS on Apple Silicon and Linux on x86-64.

uv venv --python 3.14
source .venv/bin/activate
uv pip install stable-retro-turbo
python -m stable_retro.import /path/to/your/roms

ROMs are not included. The importer matches supported ROMs to Stable Retro's game integrations.

Check the games available on your machine, then open one in the interactive player:

stable-retro-turbo play --list
stable-retro-turbo play nes
stable-retro-turbo play SuperMarioBros-Nes-v0 --press START
stable-retro-turbo play Breakout-Atari2600-v0 --mode 32 --difficulty A

Pass a full game ID such as SuperMarioBros-Nes-v0, or use all to open one imported game per platform. Add --show-obs to display the raw game beside its PPO-style preprocessed observation.

--press BUTTON[:COUNT] applies repeatable startup inputs using the selected game's own button names. Atari games additionally accept --mode N, which pulses the console SELECT switch N times before RESET, and --difficulty A or --difficulty B, which sets both console difficulty switches. For example, Breakout mode values 0, 4, 8, …, 44 select its twelve one-player cartridge variants. Use --state none to launch a game from its power-on state.

Use

import gymnasium as gym
import numpy as np

env = gym.make_vec(
    "stable_retro:StableRetro-Turbo-v0",
    game="SuperMarioBros-Nes-v0",
    state="Level1-1",
    num_envs=32,
    num_threads=16,
    render_mode="rgb_array",
    obs_crop=(32, 0, 0, 0),
    obs_crop_mode="mask",
    obs_resize=(84, 84),
    obs_resize_algorithm="area",
    obs_grayscale=True,
    obs_layout="chw",
    frame_skip=4,
    frame_stack=4,
    maxpool_last_two=True,
    info_filter="terminal",
)

obs, infos = env.reset(seed=123)
obs, rewards, terminations, truncations, infos = env.step(
    env.action_space.sample()
)

done = terminations | truncations
if done.any():
    obs, reset_infos = env.reset(options={"reset_mask": done})

The module-qualified ID imports the package and registers the factory. This ID is vector-only and requires an explicit game; RetroVecEnv remains available for direct use. Stable Retro's existing scalar stable_retro.make() and RetroEnv APIs are unchanged and are not registered under the Turbo ID.

RetroVecEnv uses Gymnasium's disabled-autoreset semantics. A finished lane keeps its terminal observation and cannot be stepped again until it is selected by a masked reset; unselected lanes keep their emulator state, RNG stream, frame stack, and sticky-action history.

Turbo Vector API v2

RetroVecEnv implements the strict Turbo Vector API v2:

  • metadata["turbo_api_version"] is 2, metadata["transition_transport"] is "numpy", and metadata["render_modes"] advertises rgb_array.
  • Immutable capabilities and signal_schema declarations describe supported features and the dtype, shape, and reset/step availability of every signal.
  • buttons, action_mode, action_preset, action_table, action_meanings, and action_table_hash expose the resolved action semantics without provider-specific probing.
  • state_catalog is an immutable ordered tuple. Callers select reset states with an int32 state_indices array and inspect the read-only active indices with active_state_indices(); state sampling and lane routing remain caller-owned.
  • observation_ownership and observation_buffer_depth declare the exact lifetime of returned observations. Rendering is opt-in: with render_mode="rgb_array", render_lane(index) renders one lane, get_images() renders all lanes, and render() renders lane zero. With the default render_mode=None, the first two methods return None and get_images() returns one None entry per lane.

When env.supports_live_snapshots is true, live positions can be captured without advancing emulation and restored into any lane of the same environment:

capture_mask = np.zeros(env.num_envs, dtype=np.bool_)
capture_mask[0] = True
captured = env.capture_snapshots(capture_mask)

restore_mask = np.zeros(env.num_envs, dtype=np.bool_)
restore_mask[3] = True
starts = [None] * env.num_envs
starts[3] = captured[0]
obs, infos = env.reset(
    options={"reset_mask": restore_mask, "snapshots": starts},
)
env.close()

Handles are reusable, session-local, and intentionally not pickleable. A single masked reset can mix snapshot starts with ordinary state_indices; infos["start_source"] is int8: 0 means an environment state and 1 means a snapshot. Scripted scenarios and cores that cannot serialize exact state report the capability as unavailable.

The fast path also supports:

  • native crop, mask, resize, grayscale, layout conversion, frame skip, frame stack, and two-frame max-pooling;
  • ordered reset-state catalogs (including power-on as index zero when no saved state exists) with explicit per-lane state_indices; state sampling and curriculum routing stay with the caller;
  • copy-safe, safe-view, and benchmark-only unsafe-view observation ownership;
  • sticky actions, random no-op starts, reward clipping, and native info filtering;
  • Atari through the packaged Stella core, using the same RetroEnv and RetroVecEnv APIs.

The inherited RetroEnv API remains available for single-environment use. RetroVecEnv supports one player, image observations, and no movie recording.

Develop

git clone https://github.com/tsilva/stable-retro-turbo.git
cd stable-retro-turbo
uv sync --frozen

Source builds require Python 3.14, CMake, a C/C++ compiler, and the platform dependencies needed by the selected emulator cores.

Commands

Run these commands from the repository root:

uv run --frozen stable-retro-turbo play --list                                      # list imported games by platform
uv run --frozen --with pytest pytest tests/test_python/test_cli.py                   # run quick tests
uv run --frozen --with build python -m build                                        # build source and wheel artifacts

Core and semantic changes additionally use a pinned original-Stable-Retro oracle for Super Mario Bros. and Breakout. The compared fields, reproducible commands, and release receipt gate are documented in docs/semantic_oracle.md.

Benchmark

In an official correctness-gated TurboBench 1.0.0 comparison on the matched supermario/canonical-v1 workload, stable-retro-turbo==1.0.1.post37 measured 2.1248x to 2.2465x the throughput of original stable-retro==1.0.1 at 1, 16, and 32 environments. This is a workload-specific result, not a claim across all games or emulator cores. See BENCHMARKS.md for exact SPS, paired confidence intervals, protocol, host details, provenance, and the reproduction command. Install the benchmark CLI with:

uv tool install \
  --exclude-newer-package turbobench-cli=2026-08-12T00:00:00Z \
  turbobench-cli==1.0.0

Notes

  • The distribution is stable-retro-turbo; the Python package is stable_retro. The upstream-compatible retro import remains available for scalar integrations, while new code should import stable_retro.
  • RetroVecEnv implements Gymnasium's vector API directly. It is not a Stable-Baselines3 VecEnv, and Stable-Baselines3 is not a runtime dependency.
  • A scalar reset seed expands to seed + lane_index. Seed sequences must contain one integer or None per lane.
  • state_catalog preloads an ordered saved-state catalog. Select reset lanes with reset_mask and their exact catalog entries with state_indices; Turbo does not sample states.
  • capture_snapshots(mask) returns lane-aligned live handles for exact same-instance continuation. The caller owns archive selection, eviction, and curriculum policy; handle.nbytes exposes approximate payload size.
  • active_state_indices() returns a read-only NumPy view. Copy it when you need a stable snapshot.
  • Stable Retro remains the source for inherited game, integration, and emulator documentation.
  • Bundled emulator cores have their own licenses; see LICENSES.md.

Local credentials

Private local values declared in .keyenv.toml live in macOS Keychain. Run keyenv doctor to verify them and launch credential-dependent commands with keyenv run -- <command>. Python, Node, and their child processes receive the values through their normal environment APIs. Keep only public or non-secret configuration in dotenv files.

Architecture

stable-retro-turbo architecture diagram

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

stable_retro_turbo-1.0.1.post43.tar.gz (96.2 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

stable_retro_turbo-1.0.1.post43-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (91.6 MB view details)

Uploaded CPython 3.14manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

stable_retro_turbo-1.0.1.post43-cp314-cp314-macosx_14_0_arm64.whl (85.9 MB view details)

Uploaded CPython 3.14macOS 14.0+ ARM64

File details

Details for the file stable_retro_turbo-1.0.1.post43.tar.gz.

File metadata

File hashes

Hashes for stable_retro_turbo-1.0.1.post43.tar.gz
Algorithm Hash digest
SHA256 30482d237d0d78ec92d2e549b9644893912504bff078c652583e4ec0fff623c5
MD5 2364620320504eaba359ffeafd4db3a9
BLAKE2b-256 f3c68f2618b8ccd81849a230db29c29fb7e740abb85c5d3136033a0c12f08b1d

See more details on using hashes here.

Provenance

The following attestation bundles were made for stable_retro_turbo-1.0.1.post43.tar.gz:

Publisher: release.yml on tsilva/stable-retro-turbo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file stable_retro_turbo-1.0.1.post43-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for stable_retro_turbo-1.0.1.post43-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 1c1d1e4a4c83c41467e202ea813a73f588527ee5315cd37039538c6b8feb1d9a
MD5 5a7a76f604cf860e690dfe34eb15cf7d
BLAKE2b-256 7d6cd32ae2f16c8386402af57de8fa4e33388bb505fb2efcf412617a01fe2a39

See more details on using hashes here.

Provenance

The following attestation bundles were made for stable_retro_turbo-1.0.1.post43-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:

Publisher: release.yml on tsilva/stable-retro-turbo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file stable_retro_turbo-1.0.1.post43-cp314-cp314-macosx_14_0_arm64.whl.

File metadata

File hashes

Hashes for stable_retro_turbo-1.0.1.post43-cp314-cp314-macosx_14_0_arm64.whl
Algorithm Hash digest
SHA256 84ced26e0b0bf348494b8f2cd11f3da419d74e689b96d239c98f82e655a3bd59
MD5 8457f048f530ee8eb3e138a7be649fdd
BLAKE2b-256 89843a919383e4935ff3219caeee78e6df1cda014c8bd3a464e54c391702627e

See more details on using hashes here.

Provenance

The following attestation bundles were made for stable_retro_turbo-1.0.1.post43-cp314-cp314-macosx_14_0_arm64.whl:

Publisher: release.yml on tsilva/stable-retro-turbo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.3.0

2 files

1.0.1.post44

2 files

This release

1.0.1.post43 This release

3 files

1.0.1.post42

3 files

1.0.1.post41

3 files

1.0.1.post39

2 files

1.0.1.post37

2 files

1.0.1.post36

2 files

1.0.1.post35

2 files

1.0.1.post34

2 files

1.0.1.post33

2 files

1.0.1.post32

2 files

1.0.1.post31

2 files

1.0.1.post30

2 files

1.0.1.post29

2 files

1.0.1.post28

3 files

1.0.1.post27

8 files

1.0.1.post26

1 file

1.0.1.post25

3 files

1.0.1.post22

8 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page