Most film grain effects are disappointing. After years of shooting both film and digital, I got frustrated with fake-looking overlays that just added random noise to images. Real film grain comes from the physical structure of silver halide crystals in photographic emulsions. I wanted to build something that actually simulated that.
The Problem with Generic Grain
Film grain is the physical manifestation of silver halide crystals in photographic emulsions. Each film stock has distinct characteristics. The crystal structure and morphology matter. So do size distribution patterns, clustering behavior, and how density varies across the emulsion.
Most grain effects ignore this entirely. They generate uniform noise and apply it as an overlay, which looks nothing like actual film when you compare them side by side.
Building the Simulator: What Actually Matters
Here's what I learned after months of research: film grain isn't just about size. The crystal structure matters. Kodak uses cubic crystals, Fuji has their sigma crystal technology. Different shapes, different clustering patterns, completely different look.
The first version I built just generated random dots at various sizes. Looked terrible. Real grain follows specific statistical distributions. ISO 100 films have tiny, uniform crystals. ISO 400 films show a bimodal distribution—mostly small grains with some larger ones mixed in. High-speed films above 800 have wide, clustered grain patterns. Getting the clustering right took three months. The math is straightforward but making it look organic is hard.
The Film Grain Generator interface: Info panel (left) shows film stock details, canvas (center) displays generated grain, toolbox (right) contains controls and sliders, performance bar (bottom) shows generation statistics
The Technical Approach
Each film stock in the library includes authentic crystal data from manufacturer specifications. Statistical size distributions measured in micrometers. Clustering algorithms that mimic real crystal behavior. Color characteristics specific to each emulsion. All the details matter.
The Film Stock Library
I included 35 film stocks total. Here are the ones I actually shoot and care about:
Tri-X 400 - It's a cliché for a reason. Everyone shoots Tri-X because the grain structure is unmatched. Bold, contrasty, looks right in almost any lighting.
HP5 Plus - I push this to 1600 constantly. More forgiving than Tri-X, grain stays manageable even at +2 stops.
T-Max 100 - When you need ultra-fine grain. The tabular crystals are no joke. I use this for portraits where grain would be distracting.
Acros 100 - Discontinued and I'm still bitter about it. The smoothest, most minimal grain Fuji ever made.
Portra 400 - Everyone's shooting this now. The skin tones are genuinely unmatched though. Fine grain for a 400-speed color film.
Pro 400H - Fuji discontinued this one too. Had a cooler color cast than Portra. The grain was somehow even finer. Still annoyed they killed it.
Ektar 100 - Vivid, saturated, ultra-fine grain. I don't shoot it much—the colors are almost too punchy—but the grain structure is interesting to simulate.
CineStill 800T - Cinema film respooled for still cameras. That halation effect around lights is divisive. I find it gimmicky but people love it.
The library includes other stocks—Delta 3200, various Ilford films, cinema stocks—but these are the ones I use for testing and comparison.
Film Grain Comparison
The differences between these two film stocks become immediately apparent when you examine them closely. The smooth color negative shows different opacity, color cast, grain shape, and distribution patterns compared to the ultra-fine black and white emulsion.
Note: In these examples, the density multiplier was set to 5x to make the grain differences more apparent for demonstration purposes.
Technical Implementation
Finding authentic data was harder than expected. Manufacturer datasheets have RMS granularity measurements, but those numbers don't tell you everything about how grain actually clusters or how the crystals interact. I ended up combining datasheet specs with measurements from research papers and real-world film scans.
I chose Rust for the programming because I wanted it to be fast and reliable. It's probably overkill for a grain simulator, but it guarantees the math is correct and efficient. Generating 2 million grains needs to happen in milliseconds, not seconds.
Early versions modeled grain as simple circles with varying opacity. Looked fake. Real crystals have irregular edges, they overlap in specific patterns, the color varies within each emulsion. I added all of that, which tripled the complexity but finally made it look right.
Advanced Algorithms
The simulator uses sophisticated mathematical models to recreate authentic grain behavior.
// Bimodal distribution for realistic grain size variation
fn generate_grain_sizes(film_params: &FilmParams) -> Vec {
let primary_distribution = normal_distribution(film_params.mean_size, film_params.std_dev);
let secondary_distribution = normal_distribution(film_params.large_grain_size, film_params.large_grain_std);
// 70% primary grains, 30% larger grains (typical for most films)
combine_distributions(primary_distribution, secondary_distribution, 0.7)
}
This was the hardest part. Real crystals don't distribute randomly—they cluster based on the emulsion chemistry. Fine-grain films like T-Max use Poisson clustering (a uniform randomness that looks smooth). Tri-X and HP5 show fractal patterns, meaning the grains clump together in organic, irregular shapes. Color films have spatial correlation that's even more complex. I tested five different clustering algorithms before finding ones that actually matched scanned film.
What It Actually Does
The app generates grain overlays as transparent PNGs you can blend onto your images. Generates anywhere from 50,000 to over 2 million individual grains depending on the film stock and your settings. Final render takes under 2 seconds on my machine.
Resolution caps at 2048x2048 right now. Could go higher but the file sizes get ridiculous and most people don't need it.
Works on macOS, Windows, and Linux. Outputs PNG, TIFF, or JPEG depending on what you're doing with it.
How I Use It
I shoot both film and digital. When I'm working on digital images that need to match film scans, this gives me grain that actually looks right. I generate a transparent overlay, blend it on in Photoshop or Affinity Photo, done.
Current limitation: The grain doesn't respond to your image content yet. It's uniform across the whole frame. Real film shows more grain in shadows and midtones, less in highlights. I'm working on that but it's complicated.
Some video people have been using it for post-production overlays. A few designers requested high-res grain textures for print work. I built it for photo work but apparently it's useful for other stuff too.
What's Missing
Lots of things still don't work perfectly:
- Grain density is uniform across the frame. Real film varies by exposure zones.
- Color grain needs work. The algorithms are simpler than B&W because color emulsions are harder to model.
- Some rare stocks aren't included. I focused on films I can actually buy or have shot.
- The halation simulation for CineStill is approximate at best.
- No integration with editing software. You export an overlay and blend it manually.
It's better than random noise. It's not perfect. If you've shot real film you'll still see the difference, but at least it's in the right direction.
Why This Matters
Film stocks keep getting discontinued. Acros, Pro 400H, dozens of others. Once they're gone, these grain characteristics disappear too. Documenting them means future photographers can at least see what different emulsions looked like, even if they can't shoot them anymore.
Plus, if you've actually shot film, generic noise looks wrong immediately. This looks closer to right.
Download
It's on GitHub. Source code is available if you want to see how it works or contribute. Bug reports and film stock requests welcome.