{"course":{"slug":"motion-and-audio","title":"Motion, Sound and Music","url":"https://addaly.com/learn/motion-and-audio","level":"intermediate","track":"create"},"lesson":{"slug":"alpha-and-premultiplication","title":"The dark fringe and what causes it","url":"https://addaly.com/learn/motion-and-audio/alpha-and-premultiplication","position":29,"of":72,"minutes":10,"answer":"Premultiplied RGB has already been multiplied by alpha, so compositing it with the straight formula multiplies the edges twice: a dark fringe means premultiplied treated as straight, a bright halo means the reverse.","keyIdea":"Premultiplied RGB has already been multiplied by alpha, so compositing it with the straight formula multiplies the edges twice: a dark fringe means premultiplied treated as straight, a bright halo means the reverse, and colour correction belongs between an unpremultiply and a re-premultiply."},"body":{"format":"markdown","text":"## The dark fringe that will not go away\n\nYou place a rendered logo with transparency over a bright photograph. Around every edge is a thin dark outline that nobody put there. You blur it, you choke the matte, you try a different export, and it persists.\n\nThis is premultiplication, it is the most common bug in compositing, and once you know the arithmetic it takes five seconds to diagnose.\n\n## Two conventions for the same file\n\nAn RGBA image stores four numbers per pixel. There are two conventions for what the RGB values mean at partially transparent pixels.\n\n**Straight** (also called unassociated, or unpremultiplied). RGB holds the pixel's true colour, regardless of transparency. A half-transparent pure red pixel stores `R=1, G=0, B=0, A=0.5`. The colour is complete; the alpha is a separate instruction.\n\n**Premultiplied** (associated). RGB has already been multiplied by alpha. The same pixel stores `R=0.5, G=0, B=0, A=0.5`. Where alpha is zero, RGB is necessarily black, because anything times zero is zero.\n\nBoth are valid. Neither is better in general. The file format usually does not tell you which one you have, and that is the whole problem.\n\n> Figure: The fringe tells you which way round the mistake is. A two by two grid, rows Straight and Premultiplied, columns Read as straight and Read as premultiplied: Straight and Read as straight: Correct; Straight and Read as premultiplied: Bright halo; Premultiplied and Read as straight: Dark fringe; Premultiplied and Read as premultiplied: Correct. Straight RGB holds the pixel's true colour whatever its alpha; premultiplied RGB has already been multiplied by it. Composite a premultiplied file with the straight formula and every edge pixel is multiplied a second time. Go the other way and the foreground arrives at full strength over a background reduced only by one minus alpha. You never have to inspect the file: the colour of the fringe is the diagnosis.\n\n## Why the fringe appears\n\nThe over operation has a form for each convention:\n\n```\nstraight:        result = FG × α  +  BG × (1 − α)\npremultiplied:   result = FG      +  BG × (1 − α)\n```\n\nThe premultiplied version omits the multiply because it has already been done. Now take a premultiplied file and composite it with the straight formula. Every edge pixel gets multiplied by alpha a *second* time. At an edge pixel with alpha 0.5, the foreground contribution is a quarter of what it should be rather than a half. Darker than it should be, only at the edges, exactly proportional to the transparency.\n\n**Dark fringe = a premultiplied image treated as straight.**\n\nGo the other way (take a straight image and hand it to a premultiplied pipeline) and the foreground is added at full strength on top of a background that has only been reduced by `1 − α`. Too much light at the edges.\n\n**Bright halo = a straight image treated as premultiplied.**\n\nThat pair is the entire diagnosis. You do not need to inspect anything; the colour of the fringe tells you which way round the error is.\n\n## Where it bites in practice\n\n**3D renders are premultiplied.** Almost universally. A render from Blender, Cinema 4D or Maya with a transparent film comes out premultiplied, and if your compositor imports it as straight you get the dark edge.\n\n**Photoshop's PNG exports are straight.** So are most design-tool exports.\n\n**After Effects asks you.** On import it shows an \"Interpret Footage → Alpha\" dialogue with Straight and Premultiplied options and a \"Guess\" button. The guess is right most of the time and wrong often enough to matter. If you see the fringe, this dialogue is where you fix it.\n\n**EXR is almost always premultiplied** by convention, and the OpenEXR specification says so.\n\n## The rule that actually matters day to day\n\n**Unpremultiply before you colour correct. Re-premultiply afterwards.**\n\nHere is why. A premultiplied edge pixel's RGB is already darkened by alpha. If you now apply a gamma or a curve, you are applying it to a value that is part colour and part transparency, and the two get tangled: the correction changes the apparent edge softness, not just the colour. The result is edges that get harder or softer as you grade, which is bewildering until you know the cause.\n\nEvery node compositor has `Unpremultiply` and `Premultiply` nodes, and the standard shape of a grading branch is unpremult, grade, premult. Nuke's colour nodes even have an `(un)premult by` input to do it inline. In After Effects the same issue appears as \"the edges got worse after I added the Curves effect\".\n\n## Checking it yourself\n\nSample a pixel in a fully transparent region of your file.\n\n- If RGB is `0,0,0` where alpha is `0`, it is almost certainly premultiplied.\n- If RGB holds a real colour where alpha is `0`, it is straight.\n\n```bash\nffprobe -v error -show_entries stream=pix_fmt -of csv=p=0 render.mov\n```\n\nwill tell you the pixel format, and a viewer that shows the RGB values with alpha disabled will tell you the rest. In Blender's image editor, switching the display to \"Color\" rather than \"Color & Alpha\" shows the raw RGB, which makes this a two-second check.\n\n## The free path\n\n**Blender**'s compositor has `Alpha Convert` nodes in both directions and renders premultiplied by default. **Fusion**, inside the free DaVinci Resolve, has `AlphaDivide` and `AlphaMultiply`, which are the same operations under their older names. **ffmpeg** has `premultiply` and `unpremultiply` filters. **GIMP** and **Krita** both work internally in a defined convention and will tell you which on export. Nothing here needs a licence.\n\n## The limitation\n\nPremultiplied images lose information at the edges, and it is not recoverable. At alpha 0.1, the stored RGB is a tenth of the real colour, quantised into 8 bits, so you have roughly 25 distinct levels left to describe that pixel's colour. Unpremultiplying multiplies it back up by ten and multiplies the quantisation error with it, which is why heavily graded soft edges from 8-bit premultiplied sources go blotchy.\n\nThe fix is not a different convention; it is more bit depth. 16-bit float, which is what EXR uses, makes the problem disappear because there are enough levels left at alpha 0.1 to survive the division. This is one of the genuine, concrete reasons professional compositing happens in float rather than in 8-bit."},"check":{"question":"A rendered element composites cleanly. After a Curves adjustment is added to it, the edges become harder and slightly haloed. Why?","options":[{"text":"The curve is being applied to premultiplied values, where each edge pixel's RGB is already scaled by its alpha, so the correction acts on a mixture of colour and transparency.","correct":true},{"text":"The Curves effect is operating in a different colour space from the composite, so the edge values are being transformed twice through the transfer function.","correct":false,"tempting":"Colour space mismatches shift overall values; they do not selectively change apparent edge hardness in proportion to alpha."},{"text":"The alpha channel is being adjusted by the curve along with the colour channels, which steepens the transition from opaque to transparent.","correct":false,"tempting":"Most colour tools leave alpha untouched by default, and the symptom appears even when alpha is explicitly excluded."},{"text":"The render was written with a straight alpha but the compositor imported it as premultiplied, so every colour operation is being applied to values that have had a division by alpha applied to them first, amplifying quantisation error at the edges.","correct":false,"tempting":"Inverts the direction: the destructive division happens when unpremultiplying, and a straight file read as premultiplied produces bright edges before any grading."}]},"prev":{"slug":"what-a-matte-is","title":"The greyscale image that decides everything","url":"https://addaly.com/learn/motion-and-audio/what-a-matte-is"},"next":{"slug":"blend-modes-are-arithmetic","title":"Every blend mode is one line of arithmetic","url":"https://addaly.com/learn/motion-and-audio/blend-modes-are-arithmetic"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'The dark fringe and what causes it', in Motion, Sound and Music. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/motion-and-audio/alpha-and-premultiplication (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"The dark fringe and what causes it","courseTitle":"Motion, Sound and Music","module":{"index":4,"slug":"putting-two-images-together","title":"Putting two images together"},"answer":null,"keyIdea":"Premultiplied RGB has already been multiplied by alpha, so compositing it with the straight formula multiplies the edges twice: a dark fringe means premultiplied treated as straight, a bright halo means the reverse, and colour correction belongs between an unpremultiply and a re-premultiply.","hero":{"kind":"figure","figure":{"t":"figure","label":"Figure","figure":{"cols":["Read as straight","Read as premultiplied"],"rows":["Straight","Premultiplied"],"type":"matrix","cells":[["Correct","Bright halo"],["Dark fringe","Correct"]],"title":"The fringe tells you which way round the mistake is","caption":"Straight RGB holds the pixel’s true colour whatever its alpha; premultiplied RGB has already been multiplied by it. Composite a premultiplied file with the straight formula and every edge pixel is multiplied a second time. Go the other way and the foreground arrives at full strength over a background reduced only by one minus alpha. You never have to inspect the file: the colour of the fringe is the diagnosis.","cellLabels":[["nothing to fix","edges too bright"],["edges too dark","nothing to fix"]]},"alt":"Figure: The fringe tells you which way round the mistake is. A two by two grid, rows Straight and Premultiplied, columns Read as straight and Read as premultiplied: Straight and Read as straight: Correct; Straight and Read as premultiplied: Bright halo; Premultiplied and Read as straight: Dark fringe; Premultiplied and Read as premultiplied: Correct. Straight RGB holds the pixel’s true colour whatever its alpha; premultiplied RGB has already been multiplied by it. Composite a premultiplied file with the straight formula and every edge pixel is multiplied a second time. Go the other way and the foreground arrives at full strength over a background reduced only by one minus alpha. You never have to inspect the file: the colour of the fringe is the diagnosis."}},"blocks":[{"t":"h2","id":"the-dark-fringe-that-will-not-go-away","runs":[{"text":"The dark fringe that will not go away"}]},{"t":"p","runs":[{"text":"You place a rendered logo with transparency over a bright photograph. Around every edge is a thin dark outline that nobody put there. You blur it, you choke the matte, you try a different export, and it persists."}]},{"t":"p","runs":[{"text":"This is premultiplication, it is the most common bug in compositing, and once you know the arithmetic it takes five seconds to diagnose."}]},{"t":"h2","id":"two-conventions-for-the-same-file","runs":[{"text":"Two conventions for the same file"}]},{"t":"p","runs":[{"text":"An RGBA image stores four numbers per pixel. There are two conventions for what the RGB values mean at partially transparent pixels."}]},{"t":"p","runs":[{"text":"Straight","b":true},{"text":" (also called unassociated, or unpremultiplied). RGB holds the pixel’s true colour, regardless of transparency. A half-transparent pure red pixel stores "},{"text":"R=1, G=0, B=0, A=0.5","code":true},{"text":". The colour is complete; the alpha is a separate instruction."}]},{"t":"p","runs":[{"text":"Premultiplied","b":true},{"text":" (associated). RGB has already been multiplied by alpha. The same pixel stores "},{"text":"R=0.5, G=0, B=0, A=0.5","code":true},{"text":". Where alpha is zero, RGB is necessarily black, because anything times zero is zero."}]},{"t":"p","runs":[{"text":"Both are valid. Neither is better in general. The file format usually does not tell you which one you have, and that is the whole problem."}]},{"t":"h2","id":"why-the-fringe-appears","runs":[{"text":"Why the fringe appears"}]},{"t":"p","runs":[{"text":"The over operation has a form for each convention:"}]},{"t":"code","lang":"","label":"Text","text":"straight:        result = FG × α  +  BG × (1 − α)\npremultiplied:   result = FG      +  BG × (1 − α)"},{"t":"p","runs":[{"text":"The premultiplied version omits the multiply because it has already been done. Now take a premultiplied file and composite it with the straight formula. Every edge pixel gets multiplied by alpha a "},{"text":"second","i":true},{"text":" time. At an edge pixel with alpha 0.5, the foreground contribution is a quarter of what it should be rather than a half. Darker than it should be, only at the edges, exactly proportional to the transparency."}]},{"t":"p","runs":[{"text":"Dark fringe = a premultiplied image treated as straight.","b":true}]},{"t":"p","runs":[{"text":"Go the other way (take a straight image and hand it to a premultiplied pipeline) and the foreground is added at full strength on top of a background that has only been reduced by "},{"text":"1 − α","code":true},{"text":". Too much light at the edges."}]},{"t":"p","runs":[{"text":"Bright halo = a straight image treated as premultiplied.","b":true}]},{"t":"p","runs":[{"text":"That pair is the entire diagnosis. You do not need to inspect anything; the colour of the fringe tells you which way round the error is."}]},{"t":"h2","id":"where-it-bites-in-practice","runs":[{"text":"Where it bites in practice"}]},{"t":"p","runs":[{"text":"3D renders are premultiplied.","b":true},{"text":" Almost universally. A render from Blender, Cinema 4D or Maya with a transparent film comes out premultiplied, and if your compositor imports it as straight you get the dark edge."}]},{"t":"p","runs":[{"text":"Photoshop’s PNG exports are straight.","b":true},{"text":" So are most design-tool exports."}]},{"t":"p","runs":[{"text":"After Effects asks you.","b":true},{"text":" On import it shows an “Interpret Footage → Alpha” dialogue with Straight and Premultiplied options and a “Guess” button. The guess is right most of the time and wrong often enough to matter. If you see the fringe, this dialogue is where you fix it."}]},{"t":"p","runs":[{"text":"EXR is almost always premultiplied","b":true},{"text":" by convention, and the OpenEXR specification says so."}]},{"t":"h2","id":"the-rule-that-actually-matters-day-to-day","runs":[{"text":"The rule that actually matters day to day"}]},{"t":"p","runs":[{"text":"Unpremultiply before you colour correct. Re-premultiply afterwards.","b":true}]},{"t":"p","runs":[{"text":"Here is why. A premultiplied edge pixel’s RGB is already darkened by alpha. If you now apply a gamma or a curve, you are applying it to a value that is part colour and part transparency, and the two get tangled: the correction changes the apparent edge softness, not just the colour. The result is edges that get harder or softer as you grade, which is bewildering until you know the cause."}]},{"t":"p","runs":[{"text":"Every node compositor has "},{"text":"Unpremultiply","code":true},{"text":" and "},{"text":"Premultiply","code":true},{"text":" nodes, and the standard shape of a grading branch is unpremult, grade, premult. Nuke’s colour nodes even have an "},{"text":"(un)premult by","code":true},{"text":" input to do it inline. In After Effects the same issue appears as “the edges got worse after I added the Curves effect”."}]},{"t":"h2","id":"checking-it-yourself","runs":[{"text":"Checking it yourself"}]},{"t":"p","runs":[{"text":"Sample a pixel in a fully transparent region of your file."}]},{"t":"ul","items":[[{"text":"If RGB is "},{"text":"0,0,0","code":true},{"text":" where alpha is "},{"text":"0","code":true},{"text":", it is almost certainly premultiplied."}],[{"text":"If RGB holds a real colour where alpha is "},{"text":"0","code":true},{"text":", it is straight."}]]},{"t":"code","lang":"bash","label":"Bash","text":"ffprobe -v error -show_entries stream=pix_fmt -of csv=p=0 render.mov"},{"t":"p","runs":[{"text":"will tell you the pixel format, and a viewer that shows the RGB values with alpha disabled will tell you the rest. In Blender’s image editor, switching the display to “Color” rather than “Color & Alpha” shows the raw RGB, which makes this a two-second check."}]},{"t":"h2","id":"the-free-path","runs":[{"text":"The free path"}],"role":"free"},{"t":"p","runs":[{"text":"Blender","b":true},{"text":"’s compositor has "},{"text":"Alpha Convert","code":true},{"text":" nodes in both directions and renders premultiplied by default. "},{"text":"Fusion","b":true},{"text":", inside the free DaVinci Resolve, has "},{"text":"AlphaDivide","code":true},{"text":" and "},{"text":"AlphaMultiply","code":true},{"text":", which are the same operations under their older names. "},{"text":"ffmpeg","b":true},{"text":" has "},{"text":"premultiply","code":true},{"text":" and "},{"text":"unpremultiply","code":true},{"text":" filters. "},{"text":"GIMP","b":true},{"text":" and "},{"text":"Krita","b":true},{"text":" both work internally in a defined convention and will tell you which on export. Nothing here needs a licence."}]},{"t":"h2","id":"the-limitation","runs":[{"text":"The limitation"}],"role":"limit"},{"t":"p","runs":[{"text":"Premultiplied images lose information at the edges, and it is not recoverable. At alpha 0.1, the stored RGB is a tenth of the real colour, quantised into 8 bits, so you have roughly 25 distinct levels left to describe that pixel’s colour. Unpremultiplying multiplies it back up by ten and multiplies the quantisation error with it, which is why heavily graded soft edges from 8-bit premultiplied sources go blotchy."}]},{"t":"p","runs":[{"text":"The fix is not a different convention; it is more bit depth. 16-bit float, which is what EXR uses, makes the problem disappear because there are enough levels left at alpha 0.1 to survive the division. This is one of the genuine, concrete reasons professional compositing happens in float rather than in 8-bit."}]}],"check":{"question":[{"text":"A rendered element composites cleanly. After a Curves adjustment is added to it, the edges become harder and slightly haloed. Why?"}],"options":[{"text":[{"text":"The curve is being applied to premultiplied values, where each edge pixel’s RGB is already scaled by its alpha, so the correction acts on a mixture of colour and transparency."}],"correct":true,"tempting":null},{"text":[{"text":"The Curves effect is operating in a different colour space from the composite, so the edge values are being transformed twice through the transfer function."}],"correct":false,"tempting":[{"text":"Colour space mismatches shift overall values; they do not selectively change apparent edge hardness in proportion to alpha."}]},{"text":[{"text":"The alpha channel is being adjusted by the curve along with the colour channels, which steepens the transition from opaque to transparent."}],"correct":false,"tempting":[{"text":"Most colour tools leave alpha untouched by default, and the symptom appears even when alpha is explicitly excluded."}]},{"text":[{"text":"The render was written with a straight alpha but the compositor imported it as premultiplied, so every colour operation is being applied to values that have had a division by alpha applied to them first, amplifying quantisation error at the edges."}],"correct":false,"tempting":[{"text":"Inverts the direction: the destructive division happens when unpremultiplying, and a straight file read as premultiplied produces bright edges before any grading."}]}],"order":[1,0,2,3],"why":null},"prev":{"slug":"what-a-matte-is","title":"The greyscale image that decides everything","minutes":10},"next":{"slug":"blend-modes-are-arithmetic","title":"Every blend mode is one line of arithmetic","minutes":9}}}