FanGraphs Lab: A Baseball Simulator
We’re excited to announce that the latest addition to the FanGraphs Lab is a baseball simulator:

You can click on any of the day’s games to pre-fill the rosters, or build your own using current players. The simulator will then simulate a game 10,000 times.
Let’s go over how this works:
- If you select one of the day’s games, we pull in the rosters for each team. We first look to see if there’s a confirmed lineup, and if there isn’t, we use the RosterResource platoon lineup based on the handedness of the opposing starting pitcher.
- Next, we build out the probability of the following events for each possible batter/pitcher matchup: single, double, triple, home run, walk, hit-by-pitch, strikeout, and in play, outs. This is done by taking the ZiPS or Steamer platoon projections for both the batter and the pitcher, depending on the projection system you choose. For instance, if the batter hits right-handed and the pitcher throws left-handed, we would pull in the batter’s “vs L” projections and the pitcher’s “vs R” projections. For Steamer, we go a step further, determining whether the pitcher is a starter or a reliever, and then pulling the specific “vs L as Reliever” projection.
- Once we have the pitcher’s and batter’s individual platoon projections, we do an odds ratio calculation (which includes league averages), and now we have the probability of each event happening for the batter/pitcher matchup.
- We also grab each batter’s Speed Score, stolen base rate and stolen base attempts per opportunity.
- We use all that data to simulate a game. We run 10,000 simulations, and then display the results.
The result set you get is robust.
You can see the percentage chance of each team winning, including the run distribution of all the games simulated, as well as a number of team stats and their corresponding histograms:

We also have average stats for each batter and pitcher:

And if you click on a particular batter or pitcher line, we have histograms of the results for each simulation:

We also include a matchup grid, which shows all the probabilities we’re using for each matchup:

We display three sample games out of the 10,000 simulations:

Finally, if you click on any play, it will show you the probability of the events for that particular matchup:

The simulator also allows you to do fun things like see what would happen if a team entirely composed of Shohei Ohtanis faced off against another team made up entirely of Shohei Ohtanis. The possibilities are nearly endless.
It’s worth asking how we know that the simulator does a decent job of actually simulating baseball.
First, we recreated our win expectancy tables using the simulator. If the simulator is doing a good job, we would expect it to have similar win expectancies based on the base out situation, inning, and game score that we see from real baseball. As you can see, these were nearly identical, with some noise due to sample size. Even 24 million games isn’t enough to remove all the noise:

We also compared the simulator’s rest-of-season results to the Steamer rest-of-season projections:
| Team | Simulation W | Playoff Odds W | Difference |
|---|---|---|---|
| Dodgers | 102.3 | 99.8 | 2.4 |
| Braves | 91.5 | 90.6 | 0.9 |
| Yankees | 90.0 | 88.6 | 1.4 |
| Mariners | 87.8 | 85.1 | 2.7 |
| Tigers | 85.9 | 85.6 | 0.3 |
| Rangers | 85.5 | 83.7 | 1.8 |
| Guardians | 84.4 | 80.8 | 3.7 |
| Diamondbacks | 84.1 | 84.8 | -0.7 |
| Padres | 83.7 | 83.4 | 0.3 |
| Phillies | 83.7 | 82.1 | 1.6 |
| Rays | 83.7 | 81.9 | 1.8 |
| Cubs | 83.6 | 84.5 | -0.9 |
| Pirates | 83.1 | 84.2 | -1.1 |
| Royals | 81.7 | 78.3 | 3.4 |
| Red Sox | 81.5 | 81.6 | -0.1 |
| Brewers | 81.0 | 83.3 | -2.3 |
| Mets | 79.4 | 84.2 | -4.8 |
| Orioles | 79.4 | 80.8 | -1.5 |
| Athletics | 79.1 | 79.1 | 0.1 |
| Giants | 79.1 | 79.4 | -0.2 |
| Twins | 78.7 | 80.1 | -1.5 |
| Cardinals | 78.5 | 78.4 | 0.1 |
| Reds | 77.9 | 78.8 | -0.9 |
| Blue Jays | 77.1 | 81.0 | -4.0 |
| Astros | 75.0 | 77.3 | -2.3 |
| Marlins | 74.6 | 75.6 | -1.0 |
| Angels | 74.6 | 75.6 | -1.0 |
| Nationals | 73.0 | 71.9 | 1.1 |
| White Sox | 68.9 | 67.6 | 1.3 |
| Rockies | 61.0 | 62.0 | -0.9 |
Additionally, the league averages all look reasonable in terms of the simulated distribution of outcomes:
| Source | 1B | 2B | 3B | HR | BB | HBP | K | SB | CS | R/9 |
|---|---|---|---|---|---|---|---|---|---|---|
| Steamer RoS | 14.45% | 4.43% | 0.39% | 2.99% | 9.84% | 1.07% | 21.60% | 2.03% | 0.59% | 4.76 |
| Steamer | 14.76% | 4.50% | 0.42% | 3.32% | 8.75% | 1.02% | 21.31% | 2.03% | 0.59% | 4.83 |
| ZiPS | 14.08% | 4.57% | 0.58% | 3.07% | 8.77% | 1.28% | 21.77% | 2.00% | 0.59% | 4.70 |
| 2025 Stats | 14.28% | 4.23% | 0.34% | 3.09% | 8.41% | 1.05% | 22.22% | 1.88% | 0.54% | 4.52 |
| 2026 Stats | 13.79% | 4.13% | 0.30% | 2.68% | 9.89% | 1.11% | 22.63% | 1.94% | 0.53% | 4.50 |
Finally, we looked at how the full season stats in the simulator compared to Steamer’s full season projected stats:
| Metric | R-Squared |
|---|---|
| AVG | .967 |
| OBP | .913 |
| SLG | .946 |
| K% (Batters) | .990 |
| BB% (Batters) | .934 |
| K% (Pitchers) | .965 |
| BB% (Pitchers) | .965 |
| HR% (Pitchers) | .891 |
| ERA (Starter) | .934 |
| ERA (Reliever) | .384 |
Everything looks pretty good here, except for reliever ERA, which will be a point of future investigation. We are handling inherited runners properly, but reliever ERA is notoriously difficult to project due to sample size. It’s possible this just exposes one difference between running a simulation and a pure projection model.
This is very much a beta product (which is why we have it as part of the Lab), so if you see any issues or have any feature requests, just let us know, either in the comments here or using the Lab’s feedback option.
David Appelman is the creator of FanGraphs.
I think the table comparing Steamer and simulation odds is missing the Mets and Brewers.
Got that fixed (though it was a different run so numbers have changed slightly).
This is the best! I’ve been meaning to do exactly this myself for years, but haven’t found the time. This is so great.
Would you allow for exporting the results to a CSV?
We should be able to do that. What are you looking for? Mainly average player stats, or something else?
I don’t quite yet know what format would make sense, but the outcome distribution data would be ideal. To be a transparent about my end goal, I’m looking to build probabilistic projections for my personal fantasy team versus my opponents given the current head to head stats, to assist with optimizing streaming and sit/start decisions.
Kind of in a similar vein, can we save one of our simulations and view it later?
Notgraphs: In my blog I invented a team of 25 Adam Dunns as a cautionary tale.
Fangraphs: At long last, we have created a team of 25 Adam Dunns from the classic blog Dangerous Experiment: A Roster of 25 Adam Dunns.
We live in absurd times. What was one fantastical has now become reality.
Be careful with your jokes, friends, someone will take them seriously.
Best I can do is Adam Duvall. Apparently we are currently limited to active players? Would be amazing to have historical data, too.
For those who want to find out this out for historical players and like dice and paper, try Strat-o-Matic!
Or try a better game like Dynasty league if you use dice or Diamond Mind if you use the computer.
Diamond Mind Online (Imagine Sports) allows for competition between owners using historical players, as well
There’s an app and a website called pennant chase that lets you do this too. You can also pick a single franchise and then use individual seasons of each player, so your Athletics team can have 1911 Eddie Collins with elite defense alongside 1972 or 1974 Catfish Hunter. And it DOES consider defense, unlike this model.I haven’t used it in like 10 years but I recall it was free/cheap, fun, and the outcomes/stats seemed reasonable.
I haven’t seen Pennant Chase before. It’s pretty cool, but it also only simulates 1 game?
any other Time Travel Baseball nostalgia bros out there?
not sure how its modeling judge’s pitching but it seems to think in fact that a team of judges would win 55/45 against a team of ohtanis.
Oh, this is actually pretty broken with Judge pitching. I think the fallback numbers for a player who doesn’t have pitching projections aren’t working properly.
This is fixed now. You can see the results here: https://www.fangraphs.com/lab/baseball-sim?awayBat=CF%3A19755%2CSS%3A19755%2CRF%3A19755%2C1B%3A19755%2CDH%3A19755%2C3B%3A19755%2CC%3A19755%2CLF%3A19755%2C2B%3A19755&awaySrc=&awaySP=19755&awayBP=Long%2520Reliever%7C19755%2CMiddle%2520Reliever%7C19755%2CSetup%2520Man%7C19755%2CCloser%7C19755&homeBat=CF%3A15640%2CSS%3A15640%2CRF%3A15640%2C1B%3A15640%2CDH%3A15640%2C3B%3A15640%2CC%3A15640%2CLF%3A15640%2C2B%3A15640&homeSrc=&homeSP=15640&homeBP=Long%2520Reliever%7C15640%2CMiddle%2520Reliever%7C15640%2CSetup%2520Man%7C15640%2CCloser%7C15640&awayteamid=22&hometeamid=9
I see a lot of “Aaron Judge stole second; Aaron Judge out”
It looks like it conflates the stolen base event with the current at bat when the guy on base is the same guy as the batter.
That’s probably because Aaron Judge is on 1st. (And at-bat). The stolen base description is always “before” the plate appearance outcome, but it’s also a different Aaron Judge on 1B than the one at bat, if that makes sense. Duplicate players was a weird challenge….
Makes sense, the other oddity I get playing around with it some, is that it appears to give no advantage at all to the home team (if anything, the away team is advantaged).
That’s true, there isn’t HFA in these at all. Something about using starter /reliever projections is causing the home team to win 49.7% of the time or something, but if you switch to ZiPS, which does not use Starter / Reliever split projections, that’s not the case. It seems like it might be an odds ratio thing and could potentially be related to the iffy reliever ERA projections.
Still looking into it, but it didn’t seem like enough of a show stopper to not release this, especially as part of labs, which is more of a beta release space.
I just got the Ohtanis winning 72% of the time.
I don’t know. That seems low.
And Ohtani K’d vs Judge in 4 of his first 7 PA’s in the first sample game that I looked at.
I have to think that Judge’s pitching is still not being properly modeled (how could it be?).
I see that everyone has already pointed this out. Carry on
I am looking for this Simulator so I can try it out. Under what menu is this located?
Edit: found it.
Where?
https://www.fangraphs.com/lab
Toward the right on the top bar.
Very exciting stuff! Thank you!
Dammit, I have work to do today!
Fangraphs has been on an absolute tear the last year+ with new features including some (like this simulator) that I never thought would show up; thanks to the entire team! Next you’re going to tell me there’s yahoo fantasy api integration and we can run multi-year sequential projections
You didn’t answer the tease. Ohtani vs Judge
https://www.fangraphs.com/lab/baseball-sim?awayBat=CF%3A19755%2CSS%3A19755%2CRF%3A19755%2C1B%3A19755%2CDH%3A19755%2C3B%3A19755%2CC%3A19755%2CLF%3A19755%2C2B%3A19755&awaySrc=&awaySP=19755&awayBP=Long%2520Reliever%7C19755%2CMiddle%2520Reliever%7C19755%2CSetup%2520Man%7C19755%2CCloser%7C19755&homeBat=CF%3A15640%2CSS%3A15640%2CRF%3A15640%2C1B%3A15640%2CDH%3A15640%2C3B%3A15640%2CC%3A15640%2CLF%3A15640%2C2B%3A15640&homeSrc=&homeSP=15640&homeBP=Long%2520Reliever%7C15640%2CMiddle%2520Reliever%7C15640%2CSetup%2520Man%7C15640%2CCloser%7C15640&awayteamid=22&hometeamid=9
I ran one with Ohtani vs. Michael Lorenzen who is, at least theoretically, also a two-way player.
Ohtani beat Judge 72% of the time (Avg 8.4 runs – 5.2)
Ohtani beat Lorenzen 93% of the time (Avg 10.6 run – 3)
From this I conclude that the Rockies should consider trading for Aaron Judge and using him as a pitcher
Yeah, whatever they are using as the value for a position player pitching, it is still too high. Ohtani is not likely to score more against Lorenzen pitching than against Judge.
Edited to add: On the other hand, being off on position player pitching is a minor bug at most.
Yeah, it’s definitely too high. We’ll need to set an actual flag for that and adjust accordingly. I put it on the list.
Love this! Any consideration in adding weather data to the simulations?
Best 4/20 present a guy could get
I think I speak for all Fangraphs readers when I say that I want to quit my job and just run this simulator all day.
Would love an option to pull RosterResource lineups at will, in addition to today’s matchups.
This is really cool! I was just talking with my son about what a team full of Ohtani’s would do. But I’m a bit skeptical of the results from this simulation. It has a team full of Ohtani’s hitting .273/.399/.582 against a team of Judges. That’s in line with Ohtani’s ROS projections (which presumably is based on Ohtani facing primarily pitchers). That’s also worse than the league average facing position players, I believe. Assuming Judge has poor defense in the infield and at catcher, I would expect Ohtani’s hitting to be even better. The simulation thinks Ohtani would win about 72% of the time, but I’d take the over (by a lot).
Yeah, for position players pitching, it’s probably not particularly realistic. There’s no flag in the sim for position players in the pitcher slot and what that would look like from a projection standpoint. But, I’ll throw that on the list of improvements to implement, to make these types of matchups potentially be more accurate (whatever that really means).
Playing with this and I feel very apprehensive that a team composed entirely of Ohtani clones top to bottom would only have a 85% chance of winning against the Rockies.
I think that actually tracks pretty well. Great lineups get shutout by the weirdest pitchers sometimes. Good pitchers get blown up by crappy teams. 85-15 still sounds pretty accurate given that the other team is still made of up actual major leaguers. Out of 10,000 simulated games, the Ohtanis won 8500 (approximately) games. 10K games is a shade over 4+ seasons of games for the entire league. Think about how much weirdness happens over 4 full seasons.
85% win rate is very high. Maybe we can put in some options to have well below replacement level players in the sim.
This is really cool! Thank you Fangraphs! One thing that would make it even better is if there were a drag and drop feature for adjusting the order of the batting lineup. I was trying to see how the odds would change if Duran had was batting leadoff in todays Red Sox/Tigers game and I essentially had to delete every player and type in each name. Doable but if the order auto-adjusted everyone down one slot after putting Duran up top, that would have saved a lot of time.
One other question, does this account for defense? I replaced Yoshida with Ceddanne Rafeala and their odds decreased from 53.8% to 53.4%. I could see that from a purely offensive standpoint but would have thought the improved defense would have more than balanced that out. Perhaps it just thinks that Ceddanne is the DH though as there’s no way to assign defensive positions to anyone.
There is no fielding taken into account. I think for a first past, the real goal was getting something reasonable credible to run very fast with a high sample size. There’s still some variation at 10k runs, but it’s not huge. You can see it from run to run if you re-sim a game.
Thanks! Overall this is excellent, I’m just getting greedy over here.
Dave, this is awesome! Would love to see the defense added so we could get some awesome stuff like a team of Mario Mendozas versus 9 Adam Dunns or Dante Bichette or whatever. I’ve always thought 9 Ichiros would be an incredible team as well with his durability, conditioning, and how hard he worked. I think he could have been at least average everywhere other than maybe C, SS, and P. He pitched as an amateur and had a good arm.
So I just ran a simulation in which a lineup of contact/speed hitters (Luiz Arraez, Steven Kwan, Xavier Edwards, Chandler Simpson, et. al.) backed by great pitching (Yoshinobu Yamamoto, Hunter Gaddis, Jason Adam, Mason Miller) played against a lineup of sluggers (Ohtani, Judge, Vlad, Soto, etc.) but with terrible pitching (Antonio Senzatela is the starter and Thomas Harrington – who? – is the closer. He did have one save in 2025!).
I ran this with Boston (the sluggers) as the home park and with Colorado (the sluggers) as the home park. In both cases, the sluggers come out on top, but not by as much as you would think!
The simulator is only returning a 52.7% win rate for the sluggers when they call Coors Field home, and a 52.2% win rate when they call Fenway home.
Is this truly the beauty of baseball? If these teams played 162 times, the slugger lineup would only be an 85-win team because pitching is that important? I love this.
No park factors yet. We’re going to add that in a future build for sure.
Thanks! I wondered if playing “dress up” mattered. Still a fun thought experiment though! I would have gone for higher than 53% for the sluggers when I was guessing.
No defense yet either. Once factored in, the sluggers win rate will take another hit.
Steamer projects team Ohtani vs team Judge hitting/Skenes pitching to be close, but team Ohtani wins 52.8% of the time, ZiPS flips the script with team Judge/Skenes winning 54.9% of the time. Run scoring changes a lot between the systems, steamer has both teams scoring 5.4-5.9 r/g whereas ZiPS has them scoring 7-7.4 r/g
I would like to see this find the optimal batting order for a given lineup. Is there a way to feed it a large number of permutations?
I think you could do this, but with the way the simulator is set up, it’s probably too slow to make front facing, and I think you can simulate a lineup just as well with markov chains, rather than brute forcing it.
This is fantastic!
one request: in addition to the projection systems, could we get previous season and current season stats?
This is so fun. I made a matchup of Mason Miller starting with a lineup made up of the top k% batters with 30 PAs vs Martin Perez starting with a lineup of the lowest k% hitters with at least 30 PAs. The Mason Miller team won, but Martin Perez got more k’s on average! Fun!
This is too fun. Thanks so much for making and publishing.
I tried a team of Shohei Ohtanis against a team of Aaron Judges (including at all pitcher spots). It does not seem remotely plausible that SP Aaron Judge would have a 7.66 ERA (even if Closer Aaron Judge would have an 8.77 ERA). Also, that Pitcher Judge would strike out Ohtani 25% of the time seems highly questionable.
So if we are doing the 26 A vs 26 B matchups, how are we handling pitcher hitting? I see Skenes has a 53-47 edge over Skubal, but it kind of looks like they just show average hitting (.315 OBP) and then ignore it and go with the pitcher rating only, as combined is the same as pitcher?
Ideally, Skenes’ hitting and defense should be better as he was a catcher in college? But it’s hard to get good pitcher hitting/defense metrics these days.
This is not being handled at all. If a player doesn’t have projections as a batter or a pitcher, we’re defaulting to the pitcher’s or batter’s projections.
I custom populated the lineups based on the defaults (9 lineup positions, SP, LR, MR, SM, CL) but the “Run Simulation” button isn’t activating. The Projection System and Options are selected.
What am I missing?
UPDATE: I still had to select “Home Team” and “Away Team”. Resolved.