Retro CRT distortion effect in Flixel 2.5

A couple of weeks ago I started using the Flixel Framework, a set of class and libraries in ActionScript3 to effectively design videogames in Flash. Flixel is suggested to all the programmers who want to create games with a retro-style flavour. Although there is a huge support community that actively works on Flixel, finding the right effect is not always easy. After looking for few hours desperately trying to find a way to implement a CRT distortion effect, I decided to create it myself! The result is a class called “RetroEffect“.

The inspiration for this work comes from a great article by Cadin Batrack, called  “Create a retro CRT distortion effect using RGB shifting“. Although, the version I create is integrated to Flixel 2.5, and has several performance improvements that makes is lighter to execute with a high framerate. In old CRT monitors, the image is composite using three different colour layers. When they are not perfectly synchronised, the result is something like this one:

Retro CRT distortion effect by Cadin Batrack

The way this effect is implemented consists in extracting the red, green and blue channel from the original image and re-drawing them a little bit distorted. The solution proposed by Cadin Batrack uses the Tweener library. Despite its being really effective, is also extremely heavy and its usage is not advisable where performance should be an issue. I used a very light version that simply cause the channel to “oscillate” following a sinusoidal path. The effect does not reach the same yeah-yeah level of awesomeness, but it can be a good base for further versions.

The code that does the trick is;

[sourcecode language=”as3″] &amp;lt;br /&amp;gt;&lt;br /&gt;<br />
// Red channel ——————————————&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
_buffer.fillRect(source.rect, 0xFF000000);&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
_buffer.copyChannel&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
(   source, source.rect, _zeroPoint,&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
BitmapDataChannel.RED, BitmapDataChannel.RED );&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
_bitmap.alpha = randRange(8, 10) / 10;&amp;lt;/p&amp;gt;&lt;br /&gt;<br />
&amp;lt;p&amp;gt;_distortion.a = sinusoid(_counter + 0 / 5, 0.99, 1.00 , 0.5);&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
_distortion.d = sinusoid(_counter + 2 / 5, 1, 1.01 , 0.5);&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
_output.draw(_bitmap, _distortion, null, null, null, true);&amp;lt;/p&amp;gt;&lt;br /&gt;<br />
&amp;lt;p&amp;gt;// Green channel ——————————————&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
_buffer.fillRect(source.rect, 0xFF000000);&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
_buffer.copyChannel&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
(   source, source.rect, _zeroPoint,&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
BitmapDataChannel.GREEN, BitmapDataChannel.GREEN);&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
_bitmap.alpha = randRange(8, 10) / 10;&amp;lt;/p&amp;gt;&lt;br /&gt;<br />
&amp;lt;p&amp;gt;_distortion.a = sinusoid(_counter + 1 / 5, 0.99, 1.00 , 0.5);&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
_distortion.d = sinusoid(_counter + 1 / 5, 1, 1.01 , 0.5);&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
_output.draw(_bitmap, _distortion, null, BlendMode.SCREEN, null, true);&amp;lt;/p&amp;gt;&lt;br /&gt;<br />
&amp;lt;p&amp;gt;// Blue channel ——————————————&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
_buffer.fillRect(source.rect, 0xFF000000);&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
_buffer.copyChannel&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
(   source, source.rect, _zeroPoint,&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
BitmapDataChannel.BLUE, BitmapDataChannel.BLUE );&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
_bitmap.alpha = randRange(8, 10)/10;&amp;lt;/p&amp;gt;&lt;br /&gt;<br />
&amp;lt;p&amp;gt;_distortion.a = sinusoid(_counter + 2 / 5, 0.99, 1.00 , 0.5);&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
_distortion.d = sinusoid(_counter + 0 / 5, 1, 1.01 , 0.5);&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
_output.draw(_bitmap, _distortion, null, BlendMode.SCREEN, null, true);&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
[/sourcecode]

Colour channels are copied into “_buffer“, and all the drawing operations are made into a temporary “BitmapData” called “_output“. The sinusoid curve is feed with a counter “_counter” that indicates the number of seconds elapsed from the beginning of the game. You can play with the matrix parameters to obtain different types of effects.

The usage of this class is quite easy. It must be added as a normal sprite, specifying the camera that will be rendered with the retro CRT distortion effect. In the example below, the standard camera has been used.

[sourcecode language=”as3″]&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
var retroEffect : RetroEffect = new RetroEffect(FlxG.camera);&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
add(retroEffect);&amp;lt;br /&amp;gt;&lt;br /&gt;<br />
[/sourcecode]

You can download the “RetroEffect” class compatible with Flixel 2.5 from here.