# Film stocks and how the looks work A film look is not a colour filter. It is five things that real film does at once, and the grade copies each of them: 1. **Tone curve.** Film has a soft "toe" (blacks are a little lifted, never pure digital black) and a long "shoulder" (bright areas roll gently into white instead of clipping). Mid-tones get a little more contrast than a phone gives them. 2. **Colour response.** Each stock pushes hues in its own direction (Portra warms skin, Fuji turns greens minty, Cinestill turns shadows cyan) and is less saturated than a phone. 3. **Halation.** Bright light passes through the film, bounces off the back and comes back as a red-orange glow around highlights. Strongest on Cinestill, which has no anti-halation layer. 4. **Grain.** Clumps of silver, most visible in the mid-tones, nearly invisible in deep shadows and bright whites, and slightly coloured. 5. **Lens and scan character.** A faint glow (diffusion) and a gentle vignette. ## Choosing a look | They say or the photo is | Use | Why | |---|---|---| | Daylight portrait, travel, wedding, lifestyle, "warm", "nostalgic", "like my parents' photos" | `portra-400` | Flatters every skin tone, creamy highlights, soft contrast | | Night street, neon, cafe, concert, city lights, "moody", "cinematic night" | `cinestill-800t` | Cool shadows, red halos around every light, bigger grain | | Overcast, beach in soft light, greenery, flowers, snow, "airy", "soft", "pastel" | `fuji-400h` | Low contrast, lifted blacks, mint greens, cool clean whites | | Resort, beach at golden hour, travel film, "teal and orange", "like a movie" | `teal-amber` | Warm skin and sand against teal water, sky and shade | | Unsure | `--all` | Show all four side by side and let them choose | Rules of thumb: warm light (golden hour, indoor tungsten) suits Portra and teal-amber; cool light (overcast, shade, blue hour) suits Fuji and Cinestill. Don't put Cinestill on a bright noon photo unless they want a cold look; don't put Fuji on a photo whose point is a warm sunset. ## The four looks ### portra-400 (Kodak Portra 400, warm) The real stock: a professional colour negative made for skin. Warm-neutral, low saturation, very wide latitude, so overexposed shots still hold detail. Skies go slightly cyan, greens slightly olive. Preset: warmer white balance (+0.32), +0.12 stop exposure (Portra is usually shot a little over), gentle contrast (0.1), lifted blacks (0.035), white point 0.965, saturation 0.86, warm shadows and creamy highlights, fine grain (0.026), light halation and vignette. ### cinestill-800t (Cinestill 800T, night tungsten) The real stock: motion-picture film balanced for tungsten light, sold for stills with its anti-halation layer removed. Under street lights it renders cool cyan shadows, warm practical lights, and the famous red glow around every bright light. Preset: cooler white balance (-0.38), stronger contrast (0.2), cyan shadows, warm highlights, strong red halation (threshold 0.7, strength 0.34), coarser grain (0.04, size 1.4), stronger vignette. Best when the photo has visible light sources; on a flat daylight photo it only looks cold. ### fuji-400h (Fuji Pro 400H, cool pastel) The real stock: loved by wedding and fashion photographers for airy, pastel results, especially overexposed. Minty greens, clean cool whites, soft blue-green shadows, low contrast. Preset: +0.18 stop exposure, slightly cool and green-leaning white balance, very low contrast (0.04), high black lift (0.05), saturation 0.8, mint shadows, fine grain (0.024), almost no halation. ### teal-amber (cinematic) Not one stock but the grade of countless travel and resort films: skin, sand and sunlight pushed towards amber; water, sky and shadows pushed towards teal. The two sit opposite each other on the colour wheel, so the subject separates from the background. Preset: contrast 0.28, every hue pulled 45 percent of the way towards the amber/teal axis (`hue_axis` 120 degrees, `hue_pull` 0.45), teal deep shadows, amber highlights, halation 0.16, grain 0.0225 (kept light so clear skies stay clean), vignette 0.24. Skin stays on the amber side of the axis, so faces stay warm. ## Every preset value "Allowed" is what `scripts/grade.mjs` accepts (anything outside is refused with an error naming the value); "Typical" is where good looks live. | Key | Allowed | Typical | What it does | |---|---|---|---| | `exposure` | -2 to 2 (stops) | -0.3 to 0.3 | Brightness before the curve. Film is often shot slightly over. | | `temperature` | -1 to 1 | -0.4 to 0.4 | White balance. Positive is warmer (more red, less blue). | | `tint` | -1 to 1 | -0.1 to 0.1 | Positive is more magenta, negative more green. | | `contrast` | 0 to 0.6 | 0 to 0.3 | Strength of the S-curve in the mid-tones. | | `black_lift` | 0 to 0.2 | 0.015 to 0.06 | How far the darkest black is lifted. Faded, scanned-film feel. | | `white_point` | 0.8 to 1 | 0.95 to 0.985 | Brightest output. Below 1 keeps whites soft and paper-like. | | `shoulder` | 0.5 to 0.95 | 0.68 to 0.8 | Where highlights start to roll off. Lower means a longer, softer roll-off. | | `saturation` | 0 to 1.5 | 0.75 to 1.05 | Overall colour strength after the curve. 0 is black and white. | | `hue_axis` | -360 to 360 (degrees) | 110 to 130 | The warm/cool axis hues are pulled towards (about 120 is amber, its opposite teal). | | `hue_pull` | 0 to 0.8 | 0 to 0.5 | How far hues move towards that axis. 0 is off. | | `shadow_tint`, `highlight_tint` | three numbers, each -0.2 to 0.2 (R, G, B) | -0.05 to 0.05 | Split toning. Shadow tint is weighted to the deepest shadows so skin stays clean. | | `diffusion` | 0 to 0.4 | 0 to 0.12 | A soft glow, like a mist filter. | | `halation.threshold` | 0.3 to 0.99 | 0.7 to 0.85 | How bright an area must be to glow. | | `halation.radius` | 0 to 0.05 (of the long edge) | 0.01 to 0.016 | How far the glow spreads. | | `halation.strength` | 0 to 1 | 0.05 to 0.35 | How strong the glow is. | | `halation.color` | three numbers, each 0 to 1 (R, G, B) | red-orange | The glow's colour. | | `grain.amount` | 0 to 0.15 | 0.02 to 0.045 | Grain strength. | | `grain.size` | 0.3 to 4 | 1 to 1.4 | Clump size (scaled with image size). | | `grain.chroma` | 0 to 1 | 0.1 to 0.2 | How coloured the grain is. | | `vignette` | 0 to 0.6 | 0.08 to 0.25 | Darkening towards the corners. | To make a custom look, copy a preset in `assets/presets.json` under a new name and change one or two values. Pass another file with `--presets my-presets.json`. ## Situations you will meet, and what to do - **Darker skin tones.** All four looks keep skin on the warm side, but heavy teal in the shadows can make deep skin look ashy. Use `portra-400`, or `teal-amber` at `--strength 0.7`, or halve `shadow_tint` in a copy of the preset. - **Very pale skin in teal-amber** can turn orange in strong sun. Use `--strength 0.75` or lower `temperature` to 0. - **Night photos.** Phone night mode is already noisy and smoothed. Use `cinestill-800t` with `--grain 0.7` so grain doesn't stack on noise. If faces are lost in darkness, offer the relight step first. - **Overcast or flat light.** `fuji-400h` keeps it airy; Portra makes it gently warmer. Teal-amber on a grey day can look muddy. - **Snow and white sand.** Bright scenes clip easily. All presets roll highlights off, but `fuji-400h` (cool whites) looks most natural on snow; Portra makes snow look creamy on purpose. - **Golden hour and sunsets.** Portra or teal-amber. Fuji fights the warm light. - **Product photos and brand colours.** A film look changes colours on purpose. If a brand colour must stay exact (packaging, logos, a red that is the brand), grade with `--strength 0.5` or `portra-400`, compare the brand colour side by side, and say that colours shift. - **Food.** Portra at `--strength 0.8` makes food warm and appetising. Avoid teal-amber and Cinestill on food; teal on food looks spoiled. - **Screenshots, graphics and text-heavy images.** Grain and halation soften small text. Skip the grade or use `--grain 0` and `--strength 0.5`. - **Photos that already have a filter or heavy edit.** Grading on top doubles the effect. Ask for the original, or use `--strength 0.5`. - **Phone HDR photos** (flat, every shadow lifted). They gain the most from the grade; teal-amber or Portra at full strength add the depth back. - **Small images (under about 800 px).** Grain looks coarse. Use `--grain 0.6`. - **Very large images (24 MP and up).** They work; grain scales with image size. Grading takes a few seconds. - **Transparent PNGs.** Transparency is flattened. Grade the photo before cutting it out, not after. - **A set of photos (carousel, wedding, trip).** Use the same preset, strength, grain and `--seed` for every photo so the set matches. Grade in one go with a shell loop. - **Black and white.** Not one of the four looks. Make a copy of `portra-400` with `"saturation": 0` and `"hue_pull": 0` and zero tints. - **Video clips.** The same preset is turned into a 3D LUT for ffmpeg, plus halation, diffusion, vignette and moving grain. The grain on video is finer than on stills. Run it on the final edit, not on each clip, so the grade is consistent. Audio is copied when it is AAC, MP3 or ALAC, and converted to AAC otherwise (for example Opus or Vorbis from a WebM, or PCM from a MOV). - **HDR and 10-bit video.** HDR clips (PQ / HDR10 or HLG, as phones record by default) are refused with a message: an SDR film look on HDR footage comes out washed out or clipped. Export an SDR (Rec.709) copy first: on a phone turn off HDR video or share as "Most compatible"; in an editor export Rec.709 SDR. With an ffmpeg build that has `zscale` (libzimg), this common recipe also works (not run in this Skill's testing, because the test machine's ffmpeg has no zscale): `ffmpeg -i hdr.mov -vf "zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p" -c:v libx264 -crf 18 -c:a aac sdr.mp4`. 10-bit SDR clips are graded and delivered as 8-bit H.264, with a note. - **Badly lit photo.** A grade can't invent detail. Offer the relight step (see SKILL.md) or say so. ## Failure modes and fixes | Problem | Fix (one per attempt) | |---|---| | Skin looks orange | Lower `--strength` to 0.7, or `temperature` in a copy of the preset | | Skin looks grey or green | Use `portra-400`, or halve `shadow_tint` | | Grain looks like digital noise | `--grain 0.6`, or lower `grain.chroma` | | Image looks washed out | Lower `black_lift` to 0.015 or raise `contrast` by 0.05 | | Too dark or muddy | Raise `exposure` by 0.15 | | Whites look grey | Raise `white_point` to 0.985 | | Halos look like a bloom filter, not film | Lower `halation.strength` or raise `halation.threshold` | | Sky turned too teal | Lower `hue_pull` (teal-amber) or use Portra | | The whole look is too strong | `--strength 0.6` |