dimjelly campsnap .flt format reference

a plain-language guide to every data point in a CS1 / CS2 and CS Pro filter file
↩ back to all campsnap resources

What is a .flt file

A .flt file is a plain-text recipe the CampSnap camera reads to reshape every photo's color as it's taken. It has no headers, no keys in most of its lines, and no metadata: just rows of comma-separated numbers in a fixed order. The camera trusts that order completely: it does not read labels, it reads position. That's why a value in the wrong place, or a row with the wrong number of entries, produces a broken or rejected filter rather than a partial one.

Everything in this reference reflects the two formats supported by the dimjelly CampSnap filter builder: the original CS1 / CS2 format (curves and a color matrix) and the newer, simpler CS Pro format (seven flat values).

↑ back to contents

Two formats, one extension

Both file types use the .flt extension, but they are structured completely differently and are not interchangeable:

CS1 / CS2
7 rows of numbers
Full control: a global adjustment row, a 3×3 color matrix, and three 256-entry tone curves (one per channel).
CS Pro
7 labeled lines
Seven simple named values: brightness, contrast, three color gains, hue, and saturation. No curves, no matrix.

A tool that imports or generates filters usually needs to detect which format it's looking at before parsing. The reliable signal: CS Pro lines look like label:value (for example lum:0); CS1/CS2 lines are just numbers separated by commas, with the 5th line onward being very long (a 256-value curve).

↑ back to contents

CS1 / CS2 — file shape

This is the original, full-control format. It has exactly seven lines, and each one has a fixed number of comma-separated values. A camera reading this file has no way to tell a short row from a shifted one, since it just counts commas, so the value count per row is not a suggestion, it's the entire contract.

RowContentsValue count
1Global adjustments7 values
2–43×3 color matrix (one row per output channel)3 rows × 3 values
5–7Per-channel tone curves (R, G, B)3 rows × 256 values

That's 784 individual numbers in total (7 + 9 + 256×3). Any file claiming to be a CS1/CS2 filter should have exactly that many, no more and no fewer.

↑ back to contents

Row 1 — global adjustments

Seven values, always in this order:

brightness, contrast, saturation, hue, gammaR, gammaG, gammaB
FieldMeaningRangeNeutral
brightnessAdded to every channel before anything else. Positive lightens, negative darkens.−255 to 2550
contrastMultiplies each channel's distance from mid-gray (128). Above 1 punches up the difference between lights and darks; below 1 flattens it.0 to 21
saturationMultiplies each channel's distance from that pixel's own luma (brightness). 0 is black & white; higher is more vivid.0 to 41
hueRotates every color around the color wheel by this many degrees.0 to 3600
gammaR / gammaG / gammaBPer-channel gamma exponent. Values below 1 brighten that channel's midtones; above 1 darken them. This is a baseline that the curve rows below can override point by point.0.01 to 21

An important quirk: saturation, hue, and contrast are all defined relative to a pixel's own brightness or to mid-gray. That means a perfectly neutral gray pixel (where R, G, and B are exactly equal) passes through brightness, contrast, saturation, and hue completely unchanged. Those four settings have no effect on true neutral pixels. Any color introduced into a neutral gray has to come from the matrix or the curves below, not from row 1.

↑ back to contents

Rows 2–4 — the color matrix3×3

Three rows of three numbers. Each row corresponds to one output channel (row 2 = output red, row 3 = output green, row 4 = output blue), and each of the three numbers in that row says how much of input red, input green, and input blue gets mixed into it.

R_out row:  wR→R, wG→R, wB→R
G_out row:  wR→G, wG→G, wB→G
B_out row:  wR→B, wG→B, wB→B

The numbers are integers scaled by 1024 rather than plain fractions (fixed-point math is cheap for the camera's processor to compute; dividing by 1024 is a fast bit-shift instead of a slow floating-point division). A weight of 1024 means "100% of this input channel," and 512 means 50%.

Identity (no color mixing at all):

1024, 0, 0,
0, 1024, 0,
0, 0, 1024

A well-behaved row's three weights typically sum to around 1024, which keeps overall exposure neutral. If a row sums to less than 1024, that output channel dims; more than 1024, and it brightens (and rows with identical weights across all three collapse the image toward black & white, since every output channel would read the same luma-like blend).

Example: a pure red tint on the red channel with a whisper of blue bled in (a cool-shadow style tint).

980, 0, 44,   ← red output leans slightly toward magenta
0, 1024, 0,   ← green untouched
0, 0, 1024    ← blue untouched

The matrix applies to every pixel at every brightness level equally. Unlike the curves below, it can't distinguish shadows from highlights. If you want a tint that only shows up in bright areas while shadows stay neutral, that has to be built into the curves instead, not the matrix.

↑ back to contents

Rows 5–7 — per-channel tone curvesRGB

Three rows, each with exactly 256 values, one row per color channel, in R, G, B order. Each row is a lookup table: position i (from 0 to 255) holds the output value that input brightness i should become, for that channel only.

Identity curve (input equals output, no change at all) is simply the numbers 0 through 255 in order:

0, 1, 2, 3, 4, 5, … 253, 254, 255

Because each of the 256 positions can be set independently, a curve can do things brightness/contrast sliders can't: crush shadows to pure black while leaving highlights untouched, lift midtones without moving the black point, or make a channel diverge from the others only above a certain brightness (which is exactly how a highlight-only or shadow-only color tint gets built: make R, G, and B identical through the low end of the curve, then let them diverge higher up).

Example: a curve that keeps the bottom ~30% of the range pinned at 0 (crushed, detail-free shadows), then rises:

0, 0, 0, 0, 0, … 0, 0, 1, 3, 8, 19, 37, 63, …

The tool that generates these curves builds each one from a gamma baseline (row 1's gammaR/G/B) and then lets any of the 256 points be manually overridden on top of that baseline. A camera reading the file doesn't know or care which points were "manual". It just reads 256 numbers per channel and uses them as-is.

↑ back to contents

How the CS1 / CS2 values combine

The camera doesn't apply these three pieces independently. It runs every pixel through them in a fixed order. Understanding the order matters, because it explains some non-obvious behavior (like row 1 being a no-op on neutral grays):

brightness contrast saturation hue rotation per-channel curve (rows 5–7) color matrix (rows 2–4)

Row 1's global adjustments run first, on the raw pixel. The per-channel curves run after that, so they reshape whatever brightness/contrast/saturation/hue already produced. The color matrix runs last, mixing the already-curved R, G, and B values together.

One practical consequence: because the curves run after saturation and hue, they are the only part of the format that can introduce a color split between shadows and highlights. The matrix, running last, then applies its fixed tint uniformly across whatever tonal range the curves already shaped.

↑ back to contents

CS Pro — file shape

CS Pro is a newer, much simpler format built for the Pro model. Instead of a matrix and curves, it's just seven lines, each written as label:value. There's no ambiguity about position since every line names its own field, but the values are still plain integers, and a camera parsing this format is just as strict about needing exactly seven recognizable lines.

lum:0
contrast:0
rgain:0
ggain:0
bgain:0
hue:0
sat:0

All seven fields default to 0 when neutral, unlike CS1/CS2, where the neutral value differs field to field (1 for contrast/saturation, 0 for brightness/hue). CS Pro's scale is simply centered on zero across the board.

↑ back to contents

The seven fields

FieldMeaningRangeNeutral
lumBrightness. Added directly to every channel.−150 to 1500
contrastContrast strength. Internally becomes a multiplier from 0.5× to 2× around mid-gray.−50 to 1000
rgainRed channel gain: a straight multiplier on the red channel after brightness/contrast.−100 to 1000
ggainGreen channel gain, same idea as rgain.−100 to 1000
bgainBlue channel gain, same idea as rgain.−100 to 1000
hueHue rotation in degrees. A much narrower range than CS1/CS2's full 0–360 wheel.−60 to 600
satSaturation. Internally becomes a multiplier from 0× to 1.5×.−100 to 500

CS Pro cannot represent everything CS1/CS2 can. There are no per-channel curves and no matrix, so effects that depend on tonal range, like shadows tinted one way while highlights go another, or a crushed black point that leaves midtones untouched, simply aren't expressible in this format. The three channel gains are flat multipliers applied everywhere, all at once, regardless of how bright or dark a pixel already is.

↑ back to contents

How the CS Pro values combine

lum (brightness) contrast r/g/b gain saturation hue rotation

Notice the channel gains sit between contrast and saturation. They're a flat per-channel multiply, not a curve, so they shift a channel's intensity uniformly rather than reshaping its tone curve. There is no separate curve or matrix stage afterward; whatever these five steps produce is the final pixel.

↑ back to contents

Neutral / no-op values, all in one place

If you're hand-editing a file, generating one programmatically, or writing an importer that needs to fall back to "no change" for a missing or invalid value, these are the exact numbers that mean do nothing for each field:

FormatFieldNeutral value
CS1/CS2brightness0
CS1/CS2contrast1
CS1/CS2saturation1
CS1/CS2hue0
CS1/CS2gammaR / gammaG / gammaB1 (each)
CS1/CS2color matrixidentity: 1024,0,0, 0,1024,0, 0,0,1024
CS1/CS2each curveidentity ramp: value at position i is i
CS Proall seven fields0 (each)

These are the same defaults a well-behaved importer should fall back to, field by field, if a file has a missing or non-numeric value in an otherwise correctly-sized file, rather than rejecting the whole file or defaulting everything to the same number regardless of what that field actually represents.

↑ back to contents

Common gotchas

Position is everything, labels don't exist (CS1/CS2)

Because CS1/CS2 rows are just comma-separated numbers, a single missing or extra value anywhere in the file shifts every value after it by one position. A parser that drops invalid tokens instead of preserving their position will silently corrupt the rest of the file rather than just that one field.

File encoding

Windows text editors often save with CRLF line endings and sometimes a UTF-8 byte-order mark (BOM) at the very start of the file. Depending on how strict the camera's own parser is, either of these can cause the file to be misread or rejected outright, even though the visible numbers look completely correct.

Decimal formatting

A value like contrast being written as 1.00 instead of 1 is numerically identical but not necessarily byte-identical to what a stricter parser was written against. When in doubt, match the plain, unpadded formatting of a known-working file.

Row 1 is inert without the curves (CS1/CS2)

Some filter-generating tools write a hardcoded placeholder into row 1 (or even the literal, non-numeric string N) because they only ever computed the matrix and curves from their source material and never touched row 1 at all. If you're importing files from an unfamiliar source, don't assume row 1 is meaningful just because it parses. Check whether the tool that produced it actually calculated those seven values.

↑ back to contents

CS1 / CS2 vs CS Pro, at a glance

CS1 / CS2CS Pro
File shape7 rows of numbers7 labeled lines
Total values784 numbers7 numbers
Global adjustmentsbrightness, contrast, saturation, hue, 3× gammalum, contrast, hue, sat
Per-channel controlfull 256-point tone curve, per channelflat gain multiplier, per channel
Color mixing3×3 matrix (cross-channel bleed possible)none
Tone-dependent tinting
(e.g. tinted shadows, neutral highlights)
possible, via the curvesnot possible
Neutral/default scalevaries by field (0 or 1)0 across every field
↑ back to contents