Blazing-fast Stable Retro fork with native vectorization and preprocessing
Project description
🚀 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 numpy as np
import stable_retro as retro
env = retro.RetroVecEnv(
"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})
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.
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"] distinguishes "snapshot" from "environment".
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;
- fixed, per-lane, or weighted saved-state selection for curricula and task-conditioned training;
- 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
RetroEnvandRetroVecEnvAPIs.
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 python scripts/benchmark_vec_env.py --list-profiles # list benchmark profiles
uv run --frozen python scripts/benchmark_vec_env.py --profile supermario-level1-1 # benchmark native and classic paths
uv run --frozen --with pytest pytest tests/test_python/test_cli.py tests/test_python/test_benchmark_vec_env.py # run quick tests
uv run --frozen --with build python -m build # build source and wheel artifacts
Benchmark definitions live in scripts/benchmark_vec_env.json. Use real saved states for representative throughput measurements; State.NONE is reserved for explicit direct-ROM diagnostics and requires --allow-state-none.
Notes
- The distribution is
stable-retro-turbo; the Python package isstable_retro. The legacyretroimport remains as a compatibility shim. RetroVecEnvimplements Gymnasium's vector API directly. It is not a Stable-Baselines3VecEnv, and Stable-Baselines3 is not a runtime dependency.- A scalar reset seed expands to
seed + lane_index. Seed sequences must contain one integer orNoneper lane. state_catalogpreloads an ordered saved-state catalog. Select reset lanes withreset_maskand their exact catalog entries withstate_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.nbytesexposes 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.
Architecture
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file stable_retro_turbo-1.0.1.post36-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: stable_retro_turbo-1.0.1.post36-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 91.6 MB
- Tags: CPython 3.14, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17ac1972f7c4a9f891ce96219c7725c52bb72cf5cb424c22eb14825644e15745
|
|
| MD5 |
7579c3a3256d44715d694d050863d46f
|
|
| BLAKE2b-256 |
65379ef8bf332cf7df2c323f2f7668f87a2935f55c68e3f171f68ad01048cff4
|
Provenance
The following attestation bundles were made for stable_retro_turbo-1.0.1.post36-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
release.yml on tsilva/stable-retro-turbo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stable_retro_turbo-1.0.1.post36-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
17ac1972f7c4a9f891ce96219c7725c52bb72cf5cb424c22eb14825644e15745 - Sigstore transparency entry: 2257375988
- Sigstore integration time:
-
Permalink:
tsilva/stable-retro-turbo@9ac5a1ad678adef958b2413490cd614cb58a8e0c -
Branch / Tag:
refs/tags/v1.0.1.post36 - Owner: https://github.com/tsilva
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9ac5a1ad678adef958b2413490cd614cb58a8e0c -
Trigger Event:
push
-
Statement type:
File details
Details for the file stable_retro_turbo-1.0.1.post36-cp314-cp314-macosx_14_0_arm64.whl.
File metadata
- Download URL: stable_retro_turbo-1.0.1.post36-cp314-cp314-macosx_14_0_arm64.whl
- Upload date:
- Size: 85.9 MB
- Tags: CPython 3.14, macOS 14.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
08fe464c96ad6982dfd60f5a776802bf2f0f5ebf00c40620082b4aec61f04817
|
|
| MD5 |
e299139e34e78e800c0f84933804785e
|
|
| BLAKE2b-256 |
f9aa78b83a554933b2f1847209d5aff45dac686a5119fcd95329851feb7dbdde
|
Provenance
The following attestation bundles were made for stable_retro_turbo-1.0.1.post36-cp314-cp314-macosx_14_0_arm64.whl:
Publisher:
release.yml on tsilva/stable-retro-turbo
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stable_retro_turbo-1.0.1.post36-cp314-cp314-macosx_14_0_arm64.whl -
Subject digest:
08fe464c96ad6982dfd60f5a776802bf2f0f5ebf00c40620082b4aec61f04817 - Sigstore transparency entry: 2257375973
- Sigstore integration time:
-
Permalink:
tsilva/stable-retro-turbo@9ac5a1ad678adef958b2413490cd614cb58a8e0c -
Branch / Tag:
refs/tags/v1.0.1.post36 - Owner: https://github.com/tsilva
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9ac5a1ad678adef958b2413490cd614cb58a8e0c -
Trigger Event:
push
-
Statement type: