Jono Kafkaris
Creative Developer

Pixel Bender – Background Subtraction

First play at creating and using a filter with Pixel Bender

Best value to enter is around 40…

This comes from a proof of concept piece for a green screen job I’m working on. Initially I was trying to use just Flash to remove the background, I had some success but it was extremely slow. Started looking into Pixel Bender as an alternative solution and had some success.

I created a filter that compares each pixel of 2 images – if the pixel is within a threshold it
turns the alpha off.

The best solution I found with the green screen was to test using the Hue value of each image.

I then load this into Flash, passing it the threshold from the text field.

It does come out with jagged edges, I’m looking perhaps another filter for correcting this, but the current solution is to run the filter on a large image and then scale it down.

It also works best with a contrasting colour, like green, as seen with our chicken friend.

Here is the code for the above demo.

Full urls to background, chicken and the filter in the code below.


PS – Also for the first time tried out GreenSock’s LoaderMax, I like it, will try it out now as an alternative to BulkLoader.

package com.spinifexgroup
	import com.greensock.loading.DataLoader;
	import com.greensock.loading.ImageLoader;
	import com.greensock.loading.LoaderMax;
	import com.greensock.loading.display.ContentDisplay;

	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Shader;
	import flash.display.ShaderJob;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFieldType;

	 * @author jono kafkaris
	[SWF(backgroundColor="#CCCCCC", frameRate="30", width="500", height="400")]
	public class GreenScreen extends Sprite
		private const IMAGE_WIDTH:uint = 500;
		private const IMAGE_HEIGHT:uint = 334;
		private var _loaderMax:LoaderMax;
		private var _background:Bitmap;
		private var _foreground:Bitmap;
		private var _backgroundSubtractionShader:Shader;
		private var _filteredBitmap:Bitmap;
		private var _inputField:TextField;
		private var _button:Sprite;
		private var _threshold:uint = 0;
		public function GreenScreen()
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);

		private function init(event:Event = null) : void
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;

		private function loadContent() : void
			_loaderMax = new LoaderMax({name:"mainQueue", onProgress:progressHandler, onComplete:completeHandler, onError:errorHandler});

			_loaderMax.append(new ImageLoader("", {name:"background"}));
			_loaderMax.append(new ImageLoader("", {name:"foreground"}));
			_loaderMax.append(new DataLoader("", {name:"backgroundSubtraction", format:"binary"}));


		private function progressHandler(event:LoaderEvent):void
			trace("progress: ",;
		private function errorHandler(event:LoaderEvent):void
			trace("error occured with ", + ": ", event.text);
		private function completeHandler(event:LoaderEvent):void
			_background = ContentDisplay(LoaderMax.getContent("background")).rawContent;
			_foreground = ContentDisplay(LoaderMax.getContent("foreground")).rawContent;
			_backgroundSubtractionShader = new Shader(LoaderMax.getContent("backgroundSubtraction"));

		private function setupStage() : void
			_inputField = new TextField();
			_inputField.type = TextFieldType.INPUT;
			_inputField.border = true;
			_inputField.backgroundColor = 0xFFFFFF;
			_inputField.background = true;
			_inputField.restrict = "0-9";
			_inputField.x = 10;
			_inputField.y = IMAGE_HEIGHT + 10;
			_inputField.width = 30;
			_inputField.height = 14;
			_inputField.text = _threshold.toString();
			_button = new Sprite();
			_button.buttonMode = true;;, 0, 30, 14);;
			_button.x = _inputField.x + _inputField.width + 10;
			_button.y = _inputField.y;
			_button.addEventListener(MouseEvent.CLICK, buttonClickHandler);

			var instructions:TextField = new TextField();
			instructions.autoSize = TextFieldAutoSize.LEFT;
			instructions.x = 10;
			instructions.y = _button.y + _button.height + 10;
			instructions.text = "Type in a value in the box above and click the black button";

			_filteredBitmap = new Bitmap(pixelBender());

		private function buttonClickHandler(event:MouseEvent) : void
			_threshold = uint(_inputField.text);
			_filteredBitmap = new Bitmap(pixelBender());

		private function pixelBender():BitmapData
			var bitmapData:BitmapData = new BitmapData(IMAGE_WIDTH, IMAGE_HEIGHT, true); = [_threshold]; = _background.bitmapData; = _foreground.bitmapData;

			var shaderJob:ShaderJob = new ShaderJob(_backgroundSubtractionShader, bitmapData);


			return bitmapData;


  1. Og2t

    5 years ago

    Cool, you might want to check Kynd’s implementation out:

  2. asmedrano

    5 years ago

    very cool!

  3. Paul Hinrichsen

    5 years ago


    I am trying to run your code in Flash CS6. I set it up correctly (I think) I have the Greensock com folder in the same folder as the .fla and I have included the Greensock .swc but although I get no reported errors nothing happens – the screen is blank.

    Any chance of a zip file that works so that I can compare and see where I am going wrong?

    Having said that GREAT piece of work !



  4. Paul Hinrichsen

    5 years ago

    OK found the error !!!!!

    Its a little thing called PATIENCE !!!!

    Sometimes it takes a second or two to complete. Works GREAT though.

    Thanks again.


Leave a Reply

Your email address will not be published. Required fields are marked *