CanRun FPS Model v1.0

A small tabular model that estimates the native frame rate of a PC game at 1080p, 1440p or 4K from two things you can know before buying or installing: the PC's hardware scores and the game's Steam store requirements. It is the FPS engine of the CanRun G-Assist plugin and runs with the Python standard library alone.

Real-world accuracy: on independent benchmarks the model alone is a median 18.9% off on recent games and NVIDIA cards, and 37% to 39% off on other test sets. The full CanRun app, which adds benchmark curves and live Steam lookups, is 15.2% off end to end. Treat every prediction as a rough estimate, good for "will this run at 60 FPS?" and not for exact numbers.

Try it: the CanRun Space runs this model alone. Pick a GPU, CPU, RAM and resolution, type a game, and it reads the live Steam store specs.

Model details

Developer Grasant (CanRun)
Version 1.0, released 2026
Type Ridge regression prior plus 100 monotonic gradient-boosted trees, in log-FPS space
Inputs 21 numeric features from GPU and CPU PassMark scores, VRAM, resolution and Steam minimum and recommended specs
Output Native raster FPS (no upscaling, frame generation or ray tracing), clipped to 5 to 500
Files model.json (weights, about 920 KB) and inference.py (features and tree traversal, standard library only)
Speed About 0.06 ms per prediction on a desktop CPU
License Apache-2.0

Real-world accuracy

These tests use outside measurements that were never used for training or tuning. All errors are median absolute percentage error. "Model alone" calls inference.py with the game's store specs. "CanRun app" is the full plugin pipeline described in the next section.

Test set Points Model alone CanRun app App within 10% Measured FPS inside the app's range
TechPowerUp RTX 5070 review (March 2025), full app end to end: 24 games typed by name, 11 NVIDIA cards, 1080p and 1440p, live Steam requirements 308 n/a 15.2% 31.2% 57.1%
TechPowerUp, the 7 of those games without a CanRun benchmark curve, 19 GPUs 266 18.9% 20.8% 30.5% n/a
TechSpot reviews (2025 to 2026): Forza Horizon 6, Crimson Desert, Battlefield 6, DOOM: The Dark Ages, on NVIDIA, AMD and Intel cards 98 36.7% 32.9% 18.4% 27.6%
OpenML 42737 "fps-in-video-games": UserBenchmark and FPS Benchmark user reports (2019 to 2020), 14 games, median per hardware group 12,032 39.4% 30.7% 17.0% 34.8%

How to read these numbers:

  • Thresholds. In the end-to-end test the app put the game on the right side of 30 FPS 99.4% of the time and of 60 FPS 93.8% of the time.
  • Bias. The model underestimates: by 14% on TechPowerUp, 35% on TechSpot and 8% on OpenML. The newest games are hit hardest.
  • Noise floor. On OpenML a single user report differs from the median of identical hardware by 12.5%, so no predictor can get far below about 12% there.
  • Version mismatches. OpenML results leave out CS:GO and the original Overwatch, because Steam now lists CS2 and Overwatch 2 requirements under those app IDs. With them, the app scores 33.8% and the model 45.9% (15,987 points).
  • Not scored. 39 end-to-end cases (God of War RagnarΓΆk, Ratchet & Clank: Rift Apart) found no Steam requirements from the typed title. 56 AMD cases were skipped because the G-Assist plugin accepts only NVIDIA GPUs.

All four tests can be rerun with evaluate_demo_end_to_end.py, evaluate_external_benchmarks.py, evaluate_techspot_benchmarks.py and evaluate_openml_benchmarks.py in scripts/ds_pipeline/ of the CanRun repository.

How the CanRun app improves on the model

  1. Live Steam requirements. The app looks up the typed title in the Steam store when the check runs and maps the minimum and recommended GPU, CPU and RAM to PassMark scores, so a game can be checked on release day.
  2. Benchmark curves first. For games in CanRun's local benchmark table, the app reads that game's curve at the user's GPU score instead of predicting. On OpenML this lowers the error from 39.4% to 30.7%, and on TechSpot NVIDIA cards from 33.6% to 28.6%. In the end-to-end test the curve path scored 15.6% on 154 points.
  3. The model for everything else. Games without a curve use this model: 18.5% on 132 end-to-end points.
  4. Matching the right game. Whole-word title matching cut the end-to-end error from 35.1% to 15.2% with no model change. The old matcher sent "Assassin's Creed Mirage" to "RAGE".

The app can also be worse than the model. Its store-spec path passes fewer inputs (no VRAM, release year or genre), so on F1 24 it is 54% off while the model is 29% off.

Compared with other FPS estimators

Tool Published accuracy Test data
CanRun 15.2% to 30.7% median error (app), 18.9% to 39.4% (model) TechPowerUp, TechSpot and OpenML, above
FPSGauge 26% median error 1,990 independent results
FPSHQ 41.6% median error 1,341 independent review results
PCBottleneck 21.6% mean error Its own estimate path
MyRigCheck, TechSavant about 15% (claimed) No public test set
Ramkumar et al. 2025 RΒ² 0.99 OpenML 42737, in-sample

Each tool was tested on different data, so this compares the size of the error, not a head-to-head result.

Intended use

  • A quick, local "can my PC run this game, and roughly how fast?" check before buying or installing, on desktop PCs with NVIDIA GeForce GTX or RTX cards.
  • An open baseline for FPS-estimation research: small, inspectable and dependency-free.

Out of scope

  • Exact FPS or purchase guarantees. Expect errors of 15% to 40%. Use it to compare options or check a threshold, not as a promise.
  • Laptops. Laptop GPUs run at lower power than the desktop cards they share names with, and no laptop data was tested.
  • Upscaling, frame generation and ray tracing. The output is native raster FPS. The CanRun plugin reports DLSS and path tracing separately as rough multipliers.
  • Games with misleading store specs. Predictions are only as good as the publisher's minimum and recommended listings. Store specs that are missing, outdated or copied between games produce wrong estimates.
  • Stutter, shader compilation, driver bugs and 1% lows. None of these can be predicted from store specs.

Bias and known failure modes

  • Newer games are underestimated. On 2025 and 2026 TechSpot reviews the app is about 30% low, and Forza Horizon 6 at High is 56% low.
  • Old, CPU-limited PCs are overestimated. On OpenML the app averages +22%, with Rainbow Six Siege at +139% and ARK at +116%.
  • AMD and Intel GPUs are less accurate. On TechSpot the model is 40.7% off on AMD and Intel cards against 33.6% on NVIDIA, because the training curves come mostly from NVIDIA cards.
  • 60 FPS locks are guessed from the title and genre. A locked game the rule misses can be predicted far above 60.
  • CPU scores are coarse. CPUs map to PassMark through family patterns, so two CPUs in one family can get the same score.

Quickstart

import importlib.util
from huggingface_hub import hf_hub_download

inference_path = hf_hub_download(repo_id="grasant/canrun-fps-predictor", filename="inference.py")
model_path = hf_hub_download(repo_id="grasant/canrun-fps-predictor", filename="model.json")

spec = importlib.util.spec_from_file_location("canrun_inference", inference_path)
canrun_inference = importlib.util.module_from_spec(spec)
spec.loader.exec_module(canrun_inference)

model = canrun_inference.CanRunFPSModel.from_pretrained(model_path)

# Cyberpunk 2077 at 1440p on an RTX 4070 with a Ryzen 7 7800X3D
result = model.predict_with_interval(
    gpu_name="RTX 4070",
    gpu_passmark=26950,
    gpu_vram_gb=12,
    cpu_passmark=34300,
    resolution="1440p",
    rec_gpu_passmark=19500,   # recommended: RTX 2060 SUPER class
    min_gpu_passmark=10100,   # minimum: GTX 1060 6GB
    rec_cpu_passmark=17500,
    rec_vram_gb=8,
    rec_ram_gb=16,
    release_year=2020,
    genre="Action RPG Open World Cyberpunk",
    game_name="Cyberpunk 2077",
)
print(result)
# {'expected_fps': 60.93, 'conformal_80_low': 57.8, 'conformal_80_high': 64.3, 'resolution': '1440p', 'model_version': '1.0'}
# The interval reflects the training suite only; real error is often Β±30%.

inference.py also has fetch_steam_store_metadata(app_id), which reads the release year, genre and name from Steam's public store API, and predict_fps(...), which returns the number alone. A title the 60 FPS rule recognizes is capped:

model.predict_fps(gpu_name="RTX 4090", gpu_passmark=38900, gpu_vram_gb=24, cpu_passmark=34300,
                  resolution="4K", rec_gpu_passmark=14500, min_gpu_passmark=8500,
                  game_name="Elden Ring", genre="Action RPG")
# 60.0

Training data

The training targets are mostly generated, not measured. Each of the 1,697 games gets an FPS curve over 25 GPUs and 3 resolutions from a power law anchored at the RTX 4090:

FPS(GPU)=anchor4090β‹…(PassMarkGPU38,192)Ξ²\text{FPS}(\text{GPU}) = \text{anchor}_{4090} \cdot \left(\frac{\text{PassMark}_{\text{GPU}}}{38{,}192}\right)^{\beta}

The anchor and the exponent Ξ², set per resolution, come from one of three sources:

Source Games Where the anchor and exponent come from
Measured benchmarks 21 Published benchmark results, with missing GPUs and resolutions filled in by the formula
Hand-calibrated catalog 58 Set by hand per game (RTX 4090 FPS and scaling exponent)
Steam store specs 628 A fixed rule on the recommended GPU's PassMark score, targeting roughly 68 FPS at 1080p on the recommended GPU
Placeholder specs by release year 990 The same rule, on minimum and recommended specs picked from the release year (for example GTX 1060 6GB recommended for 2018 to 2021), because no store specs were parsed

Special-case rules cover 60 FPS locked games and light indie games capped at 240 FPS. The 127,245 curves are then expanded over 5 CPU tiers (Ryzen 5 3600 to Ryzen 7 7800X3D) with a CPU-bottleneck factor, giving 636,225 rows. Store specs come from Steam's public store API, and game discovery uses Steam Web API chart endpoints with the developer's own key.

What this means for the scores. For about 95% of games the target is a fixed function of the same store-spec features the model receives, and for 990 of them those specs are placeholders too. The cross-validation below therefore mostly measures how well the model reproduces that rule. That is why it reports about 1% error while independent benchmarks show 15% to 40%. Use the real-world numbers above.

Four automated checks run before training: removal of curves copied from a generic template (143 repaired), FPS that falls from 1080p to 1440p to 4K for every game and GPU, robust repair of single-card outliers per game and resolution (6 repaired), and full coverage of resolutions and CPU tiers.

Not published. The benchmark table, the 127,245-entry lookup table and the harvested Steam store data stay private, to respect the Steam Web API Terms of Use. This repository holds only learned weights and code. To rebuild a dataset, use scripts/ds_pipeline/collect_and_audit.py in the CanRun repository with your own Steam Web API key.

Internal cross-validation

5-fold GroupKFold on the synthetic suite above. This checks that the model learned the generating rule without memorizing games or GPUs. It is not a measure of real-world accuracy.

Unseen games (grouped by game):

Model MdAPE MAPE RΒ² MAE (FPS) Monotonicity violations
Spec heuristic, 60 Γ— (user/spec)^0.85 9.49% 12.80% 0.7270 13.82 0.00%
Legacy v8.0 lookup model 15.23% 30.38% 0.0932 30.85 11.17%
Stage 0 ridge prior only 3.76% 7.24% 0.8998 7.30 0.00%
Single unconstrained GBDT 1.38% 4.58% 0.9590 4.12 0.02%
This model 1.19% 4.48% 0.9589 3.97 0.01%

Unseen GPUs (grouped by GPU):

Model MdAPE MAPE RΒ² MAE (FPS) Monotonicity violations
Spec heuristic, 60 Γ— (user/spec)^0.85 9.49% 12.80% 0.7270 13.82 0.00%
Legacy v8.0 lookup model 15.23% 30.35% 0.0949 30.79 11.16%
Stage 0 ridge prior only 3.79% 7.24% 0.9064 7.27 0.00%
Single unconstrained GBDT 3.18% 6.05% 0.9650 5.09 0.77%
This model 0.55% 1.08% 0.9987 0.90 0.00%

The legacy v8.0 lookup model looked strong on a random split (RΒ² 0.9866) and collapsed on unseen games (RΒ² 0.0932), because it memorized 15 game names. This model gets no game or GPU identifiers, which is why it holds up when grouped.

Architecture

The model predicts log FPS in two stages. A linear prior captures the power law between hardware and frame rate, and a small tree ensemble corrects what the prior misses.

flowchart LR
    Inputs["Hardware + Steam store specs"] -->|"21 features"| X["x"]
    X -->|"standardize + ridge"| S0["Stage 0: log-FPS prior"]
    X -->|"100 monotonic trees"| S1["Stage 1: log residual"]
    S0 --> Sum["clip(prior + residual)"]
    S1 --> Sum
    Sum -->|"exp, 60 FPS lock rule"| FPS["Native raster FPS"]

Stage 0, ridge prior. Frame rate scales roughly as a power of GPU throughput and inversely with pixel count:

FPS∝(GPUPassMarkRecGPUPassMark)Ξ²β‹…(PixelsPixels1080p)βˆ’Ξ³\text{FPS} \propto \left(\frac{\text{GPU}_{\text{PassMark}}}{\text{RecGPU}_{\text{PassMark}}}\right)^{\beta} \cdot \left(\frac{\text{Pixels}}{\text{Pixels}_{1080\text{p}}}\right)^{-\gamma}

In log space this is linear, so Stage 0 is a ridge regression (Ξ± = 10) on the 21 standardized features:

y^prior(x)=b+βˆ‘j=121wjxjβˆ’ΞΌjΟƒj\hat{y}_{\text{prior}}(\mathbf{x}) = b + \sum_{j=1}^{21} w_j \frac{x_j - \mu_j}{\sigma_j}

The linear prior lets the model extrapolate to GPUs faster than any in training, where trees alone would flatten out.

Stage 1, monotonic residual trees. 100 gradient-boosted trees (depth 5, at least 10 samples per leaf, learning rate 0.055, L2 1.2, Huber loss) fit the residual r_i = log(FPS_i) - yΜ‚_prior(x_i). Each tree respects the monotonic constraints in the feature table, so a faster GPU or CPU, more VRAM or a lower resolution never lowers the prediction.

y^log⁑(x)=clip(y^prior(x)+r0+βˆ‘m=1100Tm(x),β€…β€Šlog⁑5,β€…β€Šlog⁑500)\hat{y}_{\log}(\mathbf{x}) = \text{clip}\left(\hat{y}_{\text{prior}}(\mathbf{x}) + r_0 + \sum_{m=1}^{100} T_m(\mathbf{x}),\; \log 5,\; \log 500\right)

FPS^(x)=exp⁑(y^log⁑(x))\widehat{\text{FPS}}(\mathbf{x}) = \exp\left(\hat{y}_{\log}(\mathbf{x})\right)

Prediction interval. predict_with_interval adds a split-conformal band from out-of-fold log residuals in the synthetic suite. It holds the true value 80% of the time inside that suite, but only 15.4% of the time on TechPowerUp, so do not read it as a real-world 80% interval. A Β±30% band is closer to real behavior.

Features

No game names, app IDs or GPU names reach the model. All 21 features are computed in inference.py (extract_feature_vector). A constraint of +1 means the prediction can only rise with the feature, βˆ’1 that it can only fall, 0 unconstrained.

# Feature Meaning Constraint
0 log_gpu_passmark log of the GPU's PassMark G3D score +1
1 log_cpu_passmark log of the CPU's PassMark score +1
2 gpu_vram_gb GPU memory in GB +1
3 gpu_arch_code Architecture generation, 1.0 (Pascal) to 5.0 (Blackwell) +1
4 cpu_to_gpu_log_ratio 0.75 log GPU + 0.25 log CPU +1
5 gpu_tier_score GPU PassMark / 40,000 +1
6 log_pixel_ratio log(pixels / 1080p pixels): 0 at 1080p, 0.58 at 1440p, 1.39 at 4K βˆ’1
7 vram_per_mpixel VRAM per display megapixel +1
8 is_4k 1 at 4K βˆ’1
9 log_rec_gpu_passmark log PassMark of the store's recommended GPU 0
10 log_min_gpu_passmark log PassMark of the store's minimum GPU 0
11 gpu_headroom_rec log GPU βˆ’ log recommended GPU 0
12 gpu_headroom_min log GPU βˆ’ log minimum GPU 0
13 vram_headroom_gb VRAM βˆ’ recommended VRAM 0
14 spec_demand_index Weighted mix of recommended GPU, CPU, RAM, release year and genre 0
15 headroom_x_resolution gpu_headroom_rec Γ— (1 + 0.25 Γ— max(0, log_pixel_ratio)) 0
16 cpu_bottleneck_1080p log CPU / (1 + 0.35 Γ— max(0, log_pixel_ratio)) +1
17 vram_cliff_penalty VRAM shortfall against the recommendation, 35% stricter at 4K 0
18 is_capped_60 1 when the title or genre suggests a 60 FPS engine lock βˆ’1
19 genre_demand Genre load offset, βˆ’0.35 (esports) to +0.25 (AAA, sim) βˆ’1
20 cpu_headroom_rec log CPU βˆ’ log recommended CPU +1

Why plain JSON

The weights are plain JSON, and inference.py (about 420 lines of standard-library Python) computes the features and walks the trees. Nothing is unpickled, so loading the model cannot run code. It also installs into the G-Assist plugin without scikit-learn, numpy or onnxruntime. The staging script checks that inference.py gives the same features and predictions as the CanRun runtime on a set of probe cases.

Terms and trademarks

CanRun and this model are independent and not affiliated with or endorsed by Valve or NVIDIA. No Steam user data or API keys are collected or stored. Steam store data fetched at run time, and predictions derived from it, are provided as is, without warranty. Valve and Steam are trademarks of Valve Corporation. NVIDIA, GeForce, RTX, DLSS and G-Assist are trademarks of NVIDIA Corporation.

Citation

@misc{canrun_fps_model_v1_2026,
  author       = {Grasant},
  title        = {CanRun FPS Model v1.0: A Two-Stage Ridge and Monotonic GBDT Model for PC Game Frame-Rate Estimation},
  year         = {2026},
  publisher    = {Hugging Face},
  howpublished = {\url{https://huggingface.co/grasant/canrun-fps-predictor}}
}

Contact

Questions, wrong predictions and benchmark data are welcome in the Community tab.

Downloads last month
33
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support

Evaluation results

  • Median absolute percentage error (%) on TechPowerUp RTX 5070 review, 7 games outside the CanRun table (266 points)
    self-reported
    18.900
  • Median absolute percentage error (%) on TechSpot reviews 2025-2026 (98 points)
    self-reported
    36.700
  • Median absolute percentage error (%) on OpenML 42737 fps-in-video-games (12,032 points)
    self-reported
    39.400
  • Median absolute percentage error (%) on CanRun synthetic benchmark suite (636,225 rows)
    self-reported
    1.190
  • Raw-FPS R2 on CanRun synthetic benchmark suite (636,225 rows)
    self-reported
    0.959
  • Median absolute percentage error (%) on CanRun synthetic benchmark suite (636,225 rows)
    self-reported
    0.550
  • Raw-FPS R2 on CanRun synthetic benchmark suite (636,225 rows)
    self-reported
    0.999