Sepia Image Filter — Microsoft .NET / GDI+ ColorMatrix Transform via Canvas ImageData per-pixel Loop
Apply a sepia tone via the canonical Microsoft .NET / GDI+ ColorMatrix transform published by Stephen Toub in MSDN Magazine January 2005 (article 'Sepia Tone, StringLogicalComparer, and More') and widely propagated through C# / .NET image-processing tutorials since. The 3×3 colour matrix maps each (R, G, B) pixel to (R', G', B') via: R' = min(255, 0.393R + 0.769G + 0.189B); G' = min(255, 0.349R + 0.686G + 0.168B); B' = min(255, 0.272R + 0.534G + 0.131B). Each output channel is the same linear combination of the original RGB with weights heavy on green (0.769 for R', 0.686 for G', 0.534 for B' — the high-luminance channel dominates) and clamped to the 8-bit ceiling of 255. The result is a warm brown-yellow tonal range mimicking the photochemical sepia toning process (silver-image conversion in alkaline-thiosulfate or polysulfide solutions, used in 19th-20th century photography to extend archival print life by replacing metallic silver with more chemically stable silver sulfide). The conversion runs locally via WHATWG Canvas ctx.getImageData() → per-pixel matrix multiplication → ctx.putImageData(). Output preserves source format: PNG → PNG (RGBA alpha preserved); JPG → JPG quality 0.92 via canvas.toBlob (ITU-T T.81 Annex K). Microsoft's own .NET ColorMatrix documentation notes these coefficients are 'one possible solution' — they're widely adopted but not the only canonical sepia matrix; other published variants (Polaroid-style, Hunter-Lab-derived, ACES-derived) produce subtly different tonal profiles. Files never leave the device.
How to apply a sepia tone
- Drop your image onto the tool or click to browse. The browser's built-in decoder loads the source into Canvas at native dimensions.
- The tool auto-applies the .NET sepia matrix by default — toggle the Sepia/Original buttons to compare the filter against the source.
- The per-pixel loop runs the 3×3 colour matrix on every (R, G, B) triple, with each output channel a different linear combination of the source RGB; the green-channel weight dominates every output for the canonical warm brown-yellow tonal mapping.
- Preview the result. For a subtler effect, blend the sepia output with the original in an image editor at reduced opacity (e.g. 50% sepia, 50% original).
- Click Download to save the sepia copy. The original stays on disk untouched — files never leave the device.
Common use cases
- Giving portraits a vintage photographic feel reminiscent of 19th-century silver-gelatin prints without opening a full photo editor.
- Creating an 'aged photograph' aesthetic for timeline or retrospective design elements (history-of-the-company section, museum-style exhibit pages).
- Producing a warm tonal pass for a set of travel photos before posting them as a cohesive album.
- Styling a set of B-roll background photos for a presentation so they feel cohesive as a backdrop without competing with the foreground.
- Producing a single-tone preview reference for archival-print-style projects where the final output will be true photochemical sepia toning (digital sepia approximates the visual aesthetic).
Frequently asked questions
Where does the sepia matrix come from?
The 0.393/0.769/0.189 + 0.349/0.686/0.168 + 0.272/0.534/0.131 ColorMatrix was first published by Stephen Toub in MSDN Magazine January 2005 article 'Sepia Tone, StringLogicalComparer, and More' (learn.microsoft.com/en-us/archive/msdn-magazine/2005/january/net-matters-sepia-tone-stringlogicalcomparer-and-more) for the Microsoft .NET / GDI+ Graphics.DrawImage colour-matrix transform. The matrix has been widely propagated since through C# / .NET image-processing tutorials, Adobe AS3, and JavaScript canvas-filter ports. Microsoft's own documentation notes these coefficients are 'one possible solution' — there's no unique canonical sepia matrix; Polaroid-style and Hunter-Lab-derived variants exist with subtly different tonal profiles.
How does sepia differ from grayscale?
Grayscale (cluster 15 OI grayscale-image tool) keeps only the luminance Y per ITU-R BT.601 (0.299R + 0.587G + 0.114B) — all three output channels equal Y. Sepia keeps three distinct output channels but each is a green-weighted linear combination of the source RGB, producing a warm brown-yellow tonal range rather than pure greys. The sepia transform is what grayscale becomes if you remap the single Y channel back to (R', G', B') with the green-dominant weighting pattern characteristic of photographic silver-sulfide toning.
Can I adjust the sepia strength?
This tool applies the fixed Microsoft .NET / GDI+ matrix without a strength slider. For a subtler effect, blend the sepia output with the original in an image editor at reduced opacity (e.g. layer sepia at 50% over the original to get half-strength sepia). For a stronger effect, apply the sepia twice in sequence — but the matrix is non-linear at the clamp boundaries (values that hit 255 stay at 255), so double-sepia produces a non-uniform amplification.
Is conversion reversible?
No. The 3×3 matrix has linearly-dependent rows (each output channel is approximately green-weighted) so the mapping `(R, G, B) → (R', G', B')` is many-to-one — multiple source colours map to the same sepia output. From a sepia value alone, there's no way to recover the original (R, G, B) triple. Keep the original colour file if you may need the colour version later.
Is my image uploaded?
No. The decode + ctx.getImageData + per-pixel matrix multiplication + ctx.putImageData + canvas.toBlob chain all run client-side via WHATWG Canvas. DevTools Network tab shows zero upload requests during conversion.
Microsoft .NET / GDI+ ColorMatrix sepia transform + photochemical sepia heritage
The sepia transform applies the canonical Microsoft .NET / GDI+ ColorMatrix coefficients first published in Stephen Toub's MSDN Magazine January 2005 article 'Sepia Tone, StringLogicalComparer, and More' (learn.microsoft.com/en-us/archive/msdn-magazine/2005/january). The 3×3 colour matrix maps each (R, G, B) pixel to (R', G', B') via three independent linear combinations of the source RGB triple, with the green-channel weight dominating each output channel (0.769 for R', 0.686 for G', 0.534 for B') because green carries the most perceptual luminance per ITU-R BT.601 (cluster 15 OI grayscale standard). The matrix is widely propagated through C# / .NET image-processing tutorials, Adobe AS3, GDI+ Graphics.DrawImage code samples, and JavaScript canvas-filter ports since 2005. Microsoft's own documentation acknowledges these numbers are 'one possible solution, and the numbers can be adjusted to better suit individual needs' — there's no unique canonical sepia matrix; Polaroid-style variants (slightly cooler), Hunter-Lab-derived variants (more saturated), and ACES-derived variants (HDR-aware) all produce subtly different tonal profiles. Historical context: the sepia photographic toning process originated in 19th-century silver-print photography (commercial use ~1880s) — black-and-white silver gelatin prints were immersed in alkaline-thiosulfate or polysulfide solutions that converted the metallic silver image to silver sulfide, simultaneously producing the characteristic warm brown-yellow tint AND extending archival print life by replacing the more chemically reactive metallic silver. Modern digital sepia is a colour-matrix simulation of this photochemical process; it produces the visual aesthetic but obviously doesn't replicate the archival chemistry. The conversion runs locally via WHATWG Canvas ctx.getImageData() → per-pixel matrix multiplication with 8-bit clamping → ctx.putImageData(). Output preserves source format: PNG sources stay PNG with alpha; JPG sources output JPG at quality 0.92 (ITU-T T.81 Annex K perceptual quantisation, approximately 1-2% perceptually invisible additional loss versus the source).
- Microsoft .NET / GDI+ ColorMatrix sepia transform (Toub 2005 MSDN Magazine 'Sepia Tone, StringLogicalComparer, and More')
- Per-pixel 3×3 matrix multiplication: R' = 0.393R + 0.769G + 0.189B; G' = 0.349R + 0.686G + 0.168B; B' = 0.272R + 0.534G + 0.131B (clamped to 255)
- Green-channel weight dominant in each output channel (per ITU-R BT.601 perceptual luminance heritage)
- WHATWG Canvas ctx.getImageData() + Uint8ClampedArray for-loop + ctx.putImageData()
- Toggle between sepia-on and original (single-button comparison)
- Preserves source format: PNG → PNG with RGBA alpha; JPG → JPG quality 0.92 (ITU-T T.81 Annex K)
- Live preview of the filter effect before download
- Operates in sRGB (IEC 61966-2-1:1999); source images without an embedded ICC profile are interpreted as sRGB
Free. No signup. No file uploads. Ads via AdSense (consent required).
Sources (9)
- Toub, S. (Microsoft Corporation) (2005). .NET Matters: Sepia Tone, StringLogicalComparer, and More. MSDN Magazine, January 2005 (learn.microsoft.com/en-us/archive/msdn-magazine/2005/january/net-matters-sepia-tone-stringlogicalcomparer-and-more) — the canonical source for the Microsoft .NET / GDI+ ColorMatrix sepia transform: R' = 0.393R + 0.769G + 0.189B; G' = 0.349R + 0.686G + 0.168B; B' = 0.272R + 0.534G + 0.131B. Microsoft's own documentation notes these coefficients are 'one possible solution' — they're widely propagated through C# / .NET image-processing tutorials, Adobe AS3, and JavaScript canvas-filter ports since 2005.
- Microsoft Corporation (live). System.Drawing.Imaging.ColorMatrix — Graphics.DrawImage colour-matrix transform. learn.microsoft.com/en-us/dotnet/api/system.drawing.imaging.colormatrix — the .NET / GDI+ API for applying 5×5 colour matrix transforms to images via Graphics.DrawImage; the sepia matrix here is one common 3×3 variant published in Toub 2005.
- International Telecommunication Union (ITU-R, formerly CCIR) (1982). ITU-R BT.601 — Luma weighting that dominates sepia matrix coefficients. itu.int/rec/R-REC-BT.601 — the canonical luma weighting (0.299R + 0.587G + 0.114B) underlies why the green-channel weights dominate each output channel of the sepia matrix (0.769 for R', 0.686 for G', 0.534 for B'); green carries the most perceptual luminance per BT.601.
- WHATWG (live). HTML Living Standard — Canvas 2D Context: getImageData() + putImageData(). html.spec.whatwg.org/#dom-context-2d-getimagedata + html.spec.whatwg.org/#dom-context-2d-putimagedata — per-pixel ImageData manipulation pipeline; the sepia matrix multiplies the source RGB triple by the 3×3 matrix with 8-bit clamping.
- WHATWG (live). HTML Living Standard — HTMLImageElement (browser-native raster decoding). html.spec.whatwg.org/#htmlimageelement — universal browser entry point for raster format decode.
- WHATWG (live). HTML Living Standard — HTMLCanvasElement.toBlob(). html.spec.whatwg.org/#dom-canvas-toblob — re-encodes canvas pixel buffer to Blob.
- ITU-T (CCITT) Study Group VIII & ISO/IEC JTC 1/SC 29/WG 10 (JPEG) (1992). Information technology — Digital compression and coding of continuous-tone still images: Requirements and guidelines. ITU-T Recommendation T.81 (18 September 1992) / ISO/IEC 10918-1:1994 — JPEG baseline DCT bitstream; Annex K quantisation tables.
- W3C (PNG Working Group) (2003). Portable Network Graphics (PNG) Specification (Second Edition). W3C Recommendation 10 November 2003 / ISO/IEC 15948:2004 — PNG output format for sources with alpha.
- International Electrotechnical Commission (IEC) (1999). Multimedia systems and equipment — Colour measurement and management — Part 2-1: Default RGB colour space — sRGB. IEC 61966-2-1:1999 — default 8-bit RGB colour space the Canvas 2D path operates in.
These are the W3C, ISO/IEC, ITU-T, and IETF specifications the tool implements or builds on. Locate them on w3.org, iso.org, itu.int, or datatracker.ietf.org.