in Programming

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.

💖 Support this blog

This websites exists thanks to the contribution of patrons on Patreon. If you think these posts have either helped or inspired you, please consider supporting this blog.

Twitter_logo

YouTube_logo
📧 Stay updated

You will be notified when a new tutorial is relesed!

📝 Licensing

You are free to use, adapt and build upon this tutorial for your own projects (even commercially) as long as you credit me.

You are not allowed to redistribute the content of this tutorial on other platforms. Especially the parts that are only available on Patreon.

If the knowledge you have gained had a significant impact on your project, a mention in the credit would be very appreciated. ❤️🧔🏻

Write a Comment

Comment

  1. AWESOME! I’m gonna use this baby for sure. True, Batrack’s looks prettier, more random and broken, while yours is one same pattern over and over again. But I’m using this just for 1 second, every five seconds or so, and it looks GORGEOUS.

    One thing: I had to add the randRange function, is there a reason why you didn’t put it in the class? Not complaining or anything, it’s just this one detail that would make this class really copy-paste-use.