Eric Decker on creating a 3d engine

Nowadays it’s often that I find somebody talking about something or doing something that I wish I was doing myself – but that I can’t for a number of reasons (one of them being the fact that I’ve just moved and I don’t have a real computer and Internet connection at home). The latest installment in this pattern is Eric Decker’s great posts about creating a 3d engine of his own for educational purposes, where he goes through the steps he’s taking to create such an engine in Flash. A nice read and some interesting demos.

As a side note, working with such talented people – Eric is also at Firstborn – makes me feel like such a fraud.

Tweener, 4 years later – A post mortem

Another post that has been a long time in the making.

From the start. Until a few years ago, whenever I needed some kind of animation added to the Flash-based work I developed, I used an ActionScript tweening extension I created called MC Tween. This extension was created strictly according to my own tastes; other great tweening extensions already existed at the time, but I didn’t particularly like any of them, as I considered the API for most of them too abstract for what I needed them for. MC Tween used a prototype-based approach, adding new functionality to already existing Object types such as MovieClips. Apparently a few other people agreed with my syntactical approach to programmatic tweening, so it ended up enjoying quite a popularity for a while.

In 2005, however, I started growing increasingly irritated with MC Tween. While it managed to do its job well, ActionScript syntax and paradigm changes (specially the ones introduced with AS2), coupled with my better understanding of object-oriented programming,  made me realize it didn’t quite fit my development workflow very well anymore. What’s more, MC Tween development had begun to grow increasingly stale, and due to its popularity, any sort of experimental feature change was too dangerous to even be considered. It was sort of stuck.

So in june of that year, I started an experiment, creating a new, class-based, tweening extension. Initially, it was instance-based (you created instances of tweens), but it was later changed to a static class that created all your tweenings through a single method call, addTween(), and handled additional features through a few additional methods like stopTween(), getTweens() and such.

The class went through a lot of changes, like having its own name changed (initially called Twina, then Tweener, then ZTweener, then Tweener again), its package moved around (from generic.*, to zeh.easing.*, to caurina.transitions.*) and its API changed a lot, not to mention changing from the instanced approach to a static approach, and other big internal modifications. I think the lesson here – and this is honestly the point of most of this post – is that when you create something for yourself, knowing that no one is going to look or judge the output, much less use it on their own mission-critical work, you allow yourself to be a lot more wrong simply because you’re the only one who has to accept the risk; but, in the process, you find better ways of doing something.

At the time I started this project, Fuse Kit was becoming the most popular tweening extension out there. This was something I did not want to compete with, and I specially enjoyed the freedom of creating something in an iterative fashion, for myself, and changing it to fit my needs on every new project. That’s why Tweener remained under wraps (used only by myself) for quite a while, even though I was using it on every project I had to build for Gringo (at the time).

Tweener was “made public” on January of 2007, almost two years after its first version was created. Nate Chatellier had just joined the team – and created the AS3 version of Tweener – and I thought it wasn’t fair to keep it under wraps forever. And at that point, the API wasn’t likely to change a lot, so input would be appreciated.

Tweener wasn’t something so out of this world, and its API wasn’t anything too brand new either, since the kind of loose approach it took with new tween definition could already be found on several other existing tweening extensions – even AS1-based extensions. Still, it was probably the right syntax at the right time – as it also managed to be quite popular for a while, being even ported to other languages.

That apparently was 4 years ago (judging from the logs, Tweener was started on June 15, 2005). Other tweening extensions exist today – maybe specially popular are Go, TweenLite/TweenMax, GTween, and the new BetweenAS3 – and you know what? It’s that time again. Even if it has a modern AS3 version available, many of the paradigms adopted Tweener are pretty old-fashioned now. I’ve been growing increasingly discontent with it, I’ve learned more, and it’s time to move on.

I could just move on, but I want to have a look back at what worked and what didn’t. Consider this a Tweener post-mortem – my analysis of what I’ve learned these past 4 years and how has ActionScript changed for libraries like Tweener.

So. When moving from AS2 to AS3, there’s one important thing we need to consider: errors, in AS2, are pretty silent. You can try changing properties of a null object and Flash Player will happily oblige. Sure, it won’t work since the object doesn’t even exist, but it won’t give you any sort of error message.

This creates a problem because when you expect something to work tightly, you need several levels of verification to see whether the data you’re getting is what you expect it to be, so you can warn the user otherwise. With Tweener, under AS2, I tried to play it safe for the user: more precisely, I’d check whether objects existed on every update, whether the property being tweened already had a default value.

It turns out that with AS3, this is an unneeded feature. If you try accessing objects that don’t exist, or properties that are not there, the execution of your code will break in all kinds of ways. And this is good - it helps the user understand he’s making something very, very wrong, and that he must fix it.

When ported to AS3, however, Tweener carried many of these AS2 paradigms, so it still has a lot of verifications of that kind. Try to tween the value of a property that doesn’t exist and you’ll see a custom Tweener error, not some Flash Player error.

if (rScopes[0][istr] == undefined) {
	printError("The property '" + istr + "' doesn't seem to be a normal object property of " + String(rScopes[0]) + " or a registered special property.");
}

The same thing happens on every iteration of the property update loop – it checks whether the object being updated exists, whether the property exists, etc. Which is to say, Tweener tries too hard. The side effects of this approach are decreased performance and increased file size.

Other compromise that probably has ties with this AS2 way of distrusting the API user is that, internally, Tweener tries to play as safe as possible, giving the user little room to break stuff. One of the biggest examples of this resolution is that, when you create new tweens, Tweener searches for similar tweens (tweens applied to the same object and same properties with overlapping time) so they can be deleted. While this may make sense, since it’s the expected behavior of an animation afterall, this creates two problems – first, it has never been an option, as the overwriting is always enforced. And second, it introduces a huge loss of performance when creating new tweens, specially if you already have too many tweens in place, because of all the tweens and objects it has to check for overlaps first.

I’ve long been fond of this safer approach, but with increasingly complexity of ActionScript, and increasing performance you can tap into as long as you allow yourself to walk on the edge, it becomes too much of a compromise. In the end, it’s better to make things moderately safe, but giving the user the ability to circumvent issues that may arise with default use.

That is all to say, nowadays I believe tweening overwriting should have been off by default, but offered as a feature (not surprisingly, this is the approach that classes like TweenMax take).

Comparing performance of tweening engines recently made this problem more obvious. While it still holds moderately well with actual tween updating time and pretty well with memory consumption, Tweener has a much harder time the higher the number of tweens it has to create; tween creation time gets exponentially higher, since it checks each new tween against the entire list, and with tween numbers in the thousands it’s quite often that just trying to create the new tweens will take more time than the animation itself, just hanging the player for a while.

Now, Tweener uses a single array for its tween list, and this could be resolved by clever AS3-native tricks, like using a Dictionary instead (again, not surprisingly, this is the solution TweenMax uses, and Jack Doyle was actually the first to mention the technique to me). While doing this might have served as a stopgap, the problem still remains – Tweener just tries too hard to play it safe; to guide the user by the hand, giving him no room to explore if he just wants performance and knows what he’s doing.

That’s why, with today’s state of Tweener, I think this is not a question of optimization anymore. Stopgaps could be made, like the removal of a few checks, the addition of some modern AS3 features like a Dictionary for the list, and heck, maybe even Vectors, but I honestly believe that, by design, Tweener is already past its prime, so those changes would be just that, stopgaps.

So it makes no sense to update Tweener anymore. Sure, it works (specially if you don’t need thousands of concurrent tweens), and will continue to work, but in terms of advancements, it has reached a dead-end. There are now better ways to do things – Tweener is close to that, I think, but because it doesn’t have a lot of room to maneuver, it’s best to just leave the project as it is and move on to something else.

But, here’s something.

While I won’t be changing Tweener anymore unless some new, hideous bug shows up, I’ve made a small update – a new version, 1.33.74, is available both at the Google Code project page and at the subversion server. This update keeps the default tweening overwriting, but adds a new parameter, overwrite (that, when set to true, enables the old behavior) and a new static property, autoOvewrite (that changes the default overwriting behavior). Why is that a good thing? Because it’s faster.

Tweener Comparison

On the above graphic, the blue bar means no tween, the orange bar represents Tweener 1.31.74, and the yellow bar means Tweener 1.33.74 with overwriting set to false.

This is not much, but consider this my thank-you for the support, suggestion, lessons, and overall awesomeness I’ve received over the years due to this project. My most grateful thanks not just to people who helped the project more prominently, but to everybody who helped the project in one way or another.

And since this is some sort of closure, even though this doesn’t have much to do with the post, here’s an interesting graphic I’ve been assembling over the years since Tweener has moved to the Google Code website. It compares the percentages of download of stable versions of Tweener based on the version (see the full download count). It’s slow, but we can see how AS3 is slowly dominating the statistics. It doesn’t include the version released today.

Tweener download statistics

Here’s the same graphic, but without normalization.

Tweener download statistics

As a closing note, I must add that, personally, I’m using slightly different approaches to tweening than what was introduced by Tweener. While Tweener still works and will continue to work, my own workflow has changed slightly. I never use special shortcuts for color tweening and stuff anymore, preferring instead to create separate classes and specific functionality via getter/setters, decorators and the like. Should other people be doing that too? Heck if I know, but the reason why I work in things like Tweener in the first place is because I was happy to work on something that would fit my own development flow well, and making it available to other people later if they wanted it. You can even say it’s a bit of a naive thing to do, and that’s part of the reason why it took me so long to make it public, but I prefer to use something that’s tailored to the needs of a person (as long as I agree with that person) instead of something that’s tailored to the needs of everybody. That’s not to say any of the other libraries out there do this – I wouldn’t know – but rather to explain how is my approach to that kind of library and why I prefer to move on instead of keep hammering on something I don’t think has the best approach.

Nowadays, I’m using mixed solutions for tweening: Tweener for some stuff, an experimental tweening engine I’ve built which fits my workflow very well and that I can abuse when I want top performance (because it doesn’t try anything other that I want it to) for others, and a tweening engine built by the folks from Firstborn of which I’ll be contributing to (as soon as I get to the office – from next week on probably) and that I hope I’ll be able to talk more about the future.

To be clear, there won’t be a “Tweener 2″, or anything like that.

And thanks again!

The obligatory post-EDTED post

I’ve just returned from EDTED (formerly EWD) São Paulo, where I gave the same presentation I gave last month at EWD Rio de Janeiro. Awesome event as usual, and I think my presentation was alright. Thanks to everybody who attended, it was a huge honor to be there. This post is more to let people know that the slideshow I’ve used can finally be found here (in Brazilian Portuguese). I also wrote some honest, personal impressions here (also in Brazilian Portuguese).

Flash penetration stats for March 2009 now live, graphs updated

Adobe has updated the Flash version penetration statistics, showing Flash 10 with an average 74% penetration rate for March 2009.

This sounds fine – considering Flash Player 10 was released less than 6 months before – but when updating my graphics with a historic view, it’s clear the adoption rate has slowed down a bit. Previously, it was the player with the fastest adoption rate to date; now, it has a curve that brings it on par with how version 9.115 did (the second best adoption rate curve to date).

This is speculation on my part, but the relative slow down may probably be attributed to the lack of a killer application of Flash Player 10. If I remember correctly, developers and publishers had many reasons to adopt Flash 9 (AS3, AVM2 performance) and Flash 9.115 (h.264 and hardware-accelerated video playback) – that made several websites (like MySpace) require its installation, accelerating adoption considerably. Flash 10 had nothing of the sort – and, frankly, I don’t think I’ve seen any real work done for or requiring Flash 10. That’s not to say Flash 10 lacks features – hey, I love Vectors and the new 3D capabilities, and  I can’t wait until I’m able to use it in every project – but they’re mostly aimed at developers, and most of the new player features can be emulated in Flash 9 (even if it means lower performance), giving little reason for exclusive FP10 work right now.

Still, that it managed to hold such a quick adoption rate with no artificial stimulus is probably a testament to the overall acceptability of the plugin among users. Name recognition may be helping in making people update faster than they normally would because they know what to expect.

Changing the subject just a bit, other interesting ramification is that now we’re seeing the raise of automatic player version and stat tracking. Websites like RIA Stats and StatOwl also track plugin (and plugin version) penetration, and have lower penetration numbers for Flash 10 – 64% and 55%, respectively. Food for thought.

And I still wish Google would publish global stats from its Google Analytics service.

The obligatory post-Encontro de Web Design post

Note: this post is in Brazilian Portuguese. Its contents – in which I talk about a presentation I’ve given a couple of days ago – are only important for people who live in Brazil, and as such, I’m opening another exception and writing in my own language. Sorry, everybody else. If you want, there’s a translation here.

Voltei anteontem do 14º Encontro de Web Design (EWD), no Rio de Janeiro, e agora finalmente tenho tempo pra escrever um pouquinho sobre isso.

Sem ser muito redundante e repetindo só um pouco o que já foi dito por muitos dos participantes, foi um ótimo evento. Talvez algo possa ser reformulado, como colocar tempo para perguntas e respostas no final de cada palestra – evitando misturar muitos assuntos diferentes na mesa redonda final ou fragmentar a audiência – ou adicionar mais “tracks” simultâneos no futuro, mas, no geral, eu acho que o saldo foi bastante positivo. A Cristiane, a Adriana e todos os envolvidos merecem os parabéns pela organização.

14º Encontro de Webdesign - foto por Luís Ricardo

14º Encontro de Webdesign - foto por Luís Ricardo

Eu sou especialmente agradecido pela oportunidade de ter falado lá, ainda que acredite que o conteúdo da minha apresentação – que era mais focado em um aspecto do mercado de desenvolvimento – tenha sido menos um “convite para reflexão” do que as outras palestras, em especial a do Roberto Cassano (que realmente abriu meus olhos).

Agradeço também em especial ao Bruno Ribeiro e a toda sua família por terem me recebido super bem. Eles tiveram uma papel muito importante em fazer com que esta breve visita ao Rio tenha sido tão agradável como foi.

Agora ambos o EWD e o Encontro de TI fazem as malas e preparam-se para aportar na capital Paulista – no dia 25 de Abril, teremos os mesmos 14º EWD e 2º ETI em São Paulo. Eu vou estar lá dando a mesma palestra – ainda que um pouco reformulada, e falando mais devagar. :)

Esta também é a razão para ainda não disponibilizar o slideshow da minha apresentação ainda; ele estará no ar dia 25.

Até lá!

Talking tomorrow: Encontro de Webdesign

Like I mentioned earlier, tomorrow I’ll be at Rio de Janeiro at the 14th Encontro de Webdesign (“Webdesign Meeting”), giving a presentation whose title translates to something like “Rich interfaces as tools for public involvement”. It’s basically a talk that aims to show why and how rich interface platforms (Flash and others) can be used, with a few cases studies (in Flash).

Other presentations include talks by Roberto Cassano and Gil Giardelli. There’s also an IT meetup happening simultaneously so it should be interesting. I’ll probably be writing random, disjointed posts from there using my Brazilian Portuguese Twitter account. See you there!

Online applications: raster image editors

One of the topics I’ve delved the most into while creating my graduation project last year was web-based applications – “application-like” websites in which you can do tasks that were usually restricted to full-fledged applications you usually have to install on your own computer.

In my opinion, there’s this silent revolution going on, where we’re moving many (but not all) of the tasks we usually need to do on a computer to web-based applications. Researching – and, specially, actually using those applications – made me understand the whole thing a bit better. It’s not just about the Cloud or about social media or whatever Web 2.0 is supposed to be about, it’s also about the fact that web-based applications can be pretty helpful under certain situations – something I learned first-hand when I was almost forced to switch all my in-college text editing to Google Docs, after they uninstalled Open Office from the campus’ computers.

This is a bigger topic and this article about “The Netbook Effect’ on Wired can probably explain it better than I could.

Anyway, it turns out there’s some nifty applications out there. Since I have a list of similar apps I used on my graduation project presentation, and prompted by a question from a friend, I figured I’d post them here in the hopes such a list is useful for someone else. So here it is: a list or raster image (bitmap) editors that are web-based and can be ran from anywhere with no installation. Most of them are based on Flash.

Full-fledged, Photoshop-style image editors:

Simpler, yet very useful image editors:

Tools

Experimental stuff

Have I forgotten something?

Preparing the next talk… with a little help from my (online) friends

Note: this post is in Brazilian Portuguese. Its contents – in which I ask for suggestions about an upcoming presentation I’ll give – are only important for people who live in Brazil, and as such, I’m opening an exception and writing in my own language. Sorry, everybody else. If you want, there’s a translation here.

No próximo dia 28 de março, vou dar uma palestra no 14º Encontro de Webdesign, que acontece no Rio de Janeiro. O título da palestra é “Interfaces ricas como ferramenta de envolvimento do público” e vou falar sobre como interfaces “ricas” – Flash, hoje, mas não necessariamente, amanhã – são utilizadas, em especial pelo mercado publicitário, como parte de sua estratégia online através da geração de conteúdo pelo público. Vou falar sobre o lance todo do ponto de vista de um desenvolvedor, e mostrar alguns exemplos (meus e de terceiros) de como isso tudo acontece. É uma coisa meio sobre publicidade mesmo, uma visão da indústria pra indústria (nada sobre ativismo social ou coisa assim, só pra deixar claro). Não que eu seja um amante da publicidade, já que sou programador e vou falar mais da parte prática da coisa, mas enfim.

Bom, pegando carona neste post do Roberto Cassano, e até porque o evento está focado em redes sociais, queria perguntar pro público que pretende comparecer a este evento (ou, potencialmente, ao mesmo evento, em outras localidades) o que esperam da apresentação. Na verdade, já tenho a estrutura da apresentação planejada (introdução sobre a plataforma; contextualização e comparação com outras plataformas e utilização, problemas e vantagens; vários cases; detalhes técnicos como métricas), mas ainda existem tópicos que posso diminuir ou dar mais ênfase de modo a aproveitar melhor o tempo de apresentação (45 minutos) e até porque não vai ter rodada de perguntas imediatamente após a palestra (só mais tarde, numa mesa redonda).

Não vou fazer um form do Google Docs, mas queria perguntar o seguinte: tem algo específico que vocês querem ver explicado na palestra? Tem algum assunto, polêmico ou não, que vocês acham que precisa falar? Tem algo que precisa ser explicado? Vocês detestam Flash e precisam ver razão pra ele ser usado? Vocês adoram Flash e precisam ver razão pra ele não ser usado? (E quando falo “Flash”, eu me refiro também a plataformas de “interface rica” semelhantes – estou juntando Flash, SilverLight, JavaFX e etc no mesmo saco).

Pergunto isso pra todos, mas mais quem não trabalha especificamente na área de desenvolvimento Flash (ou semelhantes) mesmo. Imagino que o público da palestra seja um público mais misto, e às vezes a gente fica meio alienado quando falamos só com nossos pares, então fico curioso pra saber se tem algo que esqueci de colocar na minha estrutura.

Sei que as perguntas são bastante genéricas, mas enfim, fica aí o pedido. Respostas podem ser colocadas nos comentários do post, enviadas pro email zeh arroba zehfernando ponto com, enviadas via Twitter, etc etc. Comentários podem ser anônimos e críticos, mas tenho alergia a flames, então keep it beautiful. Valeu!

Changing a MovieClip’s registration point painlessly

And while I’m still in the subject, here’s a final example of how one can use getter/setters do add some functionality that doesn’t normally exist in a MovieClip: altering the registration point of a MovieClip.

This is some commonly requested functionality. There are several libraries out there that do the trick, I suppose, but a long time ago, during my first AS3 project, none of them worked the way I wanted to, so I had to build my own.

It works like this: the Class PointMovieClip – again, by lack of a better name – has two new properties, registrationX and registrationY. These define where the display object’s axis is located (inside its own coordinate space), and obviously, default to 0, 0. If you change them, it’s as if you changed the registration point.

// Moves the registration point of an object 10 pixels down and to the right from its original position
myPMC.registrationX = 10;
myPMC.registrationY = 10;

// Rotates the object from the new position
myPMC.rotation = 45;

The interesting thing about this example is that it works by overriding existing properties such as x and y, and adding some additional functionality to rotation, scaleX and scaleY. The getter/setters, in this case, are intercepting the user’s changes to those values and making it act accordingly, so other than adding registrationX and registrationY, it’s maintaining the same MovieClip API, but adding new functionality to it.

package com.zehfernando.display {
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.geom.Point;	

	/**
	 * @author Zeh Fernando
	 */
	public class PointMovieClip extends MovieClip {

		// Instance properties ----------------------------------------------------------------

		protected var _registrationX:Number;		// Registration point X
		protected var _registrationY:Number;		// Registration point Y
		protected var _x:Number;					// User-defined X
		protected var _y:Number;					// User-defined Y

		protected var setToUpdate:Boolean;			// Whether this instance is already set to update on the next Event.RENDER

		// ================================================================================================================
		// CONSTRUCTOR ----------------------------------------------------------------------------------------------------

		public function PointMovieClip() {
			super();

			// Reads the current __values to keep them
			_x = super.x;
			_y = super.y;
			_registrationX = 0;
			_registrationY = 0;
			fixPosition();
		}

		// ================================================================================================================
		// INTERNAL INSTANCE functions ------------------------------------------------------------------------------------

		protected function fixPosition(): void {
			// Using localToGlobal/globalToLocal is less precise than doing it mathematically, but the end result is more accurate inside Flash because it's in sync with Flash's positioning and rotating limitations
			var op:Point = new Point(0, 0);
			var rp:Point = new Point(_registrationX, _registrationY);
			rp = parent.globalToLocal(localToGlobal(rp));
			op = parent.globalToLocal(localToGlobal(op));
			super.x = _x - (rp.x - op.x);
			super.y = _y - (rp.y - op.y);
		}

		protected function requestPositionFix(): void {
			if (Boolean(stage) && !setToUpdate) {
				setToUpdate = true;
				stage.addEventListener(Event.RENDER, onRender, false, 0, true);
				stage.invalidate();
			}
		}

		// ================================================================================================================
		// EVENT functions ------------------------------------------------------------------------------------------------

		protected function onRender(e:Event): void {
			stage.removeEventListener(Event.RENDER, onRender);
			setToUpdate = false;
			fixPosition();
		}

		// ================================================================================================================
		// ACCESSOR functions ---------------------------------------------------------------------------------------------

		override public function get x(): Number {
			return _x;
		}
		override public function set x(__value:Number): void {
			_x = __value;
			requestPositionFix();
		}

		override public function get y(): Number {
			return _y;
		}
		override public function set y(__value:Number): void {
			_y = __value;
			requestPositionFix();
		}

		override public function set rotation(__value:Number): void {
			super.rotation = __value;
			requestPositionFix();
		}
		override public function set scaleX(__value:Number): void {
			super.scaleX = __value;
			requestPositionFix();
		}
		override public function set scaleY(__value:Number): void {
			super.scaleY = __value;
			requestPositionFix();
		}

		public function get registrationX(): Number {
			return _registrationX;
		}
		public function set registrationX(__value:Number): void {
			_registrationX = __value;
			requestPositionFix();
		}

		public function get registrationY(): Number {
			return _registrationY;
		}
		public function set registrationY(__value:Number): void {
			_registrationY = __value;
			requestPositionFix();
		}

	}
}

One last note: with this class, changing the registration point’s X and Y will move the object around, as it keeps the previous x and y positions instead of offsetting them to match; this is something I actually needed in my implementation as there were moments I tweened that value (together with x, y and rotation – basically, sliding, rotating rectangles on screen) and wanted the object to respond accordingly in a smooth fashion.

While there may be moments one needs to rotate a display object from a given point with the x and y changing immediately to match, I believe the above implementation to work better when animating things across screen.

Creating a MovieClip class for designers

Riding on what I wrote yesterday about using getter/setters for actual state control, here’s another example of how getter/setters can be used to control how your display assets work and make your life simpler.

For a while, for me, it was quite common to employ Tweener’s ColorShortcuts if I wanted to, say, tween the brightness or contrast of an image in Flash. However, there was a problem if I wanted to change the image attributes without animating it; while Tweener would let me do a transition in “0″ time (applying it immediately), it’s odd that I had to use a tweening engine to do something that wasn’t related to tweening at all. Additionally, since Tweener’s color shortcuts try to apply new properties to a DisplayObject without using new variables, it was common that a special tweening would overwrite a visual effect previously applied to the object, and this is hard to solve from an external class since you don’t have a very tight control of what happens with an object.

The correct solution is having a DisplayObject class that employs all the features you want and is completely independent of the tweening engine used. This meant that, for color shortcuts, you’d ideally have a sort of a MovieClip class with attributes like brightness, contrast, etc.

I’ve used variations of this on my work for a while, but today I finally sat down and wrote a more generic display class with properties that allow anyone to adjust some of its color properties. By lack of a better name, it’s called DesignerMovieClip, and it extends MovieClip. It works exactly like the class it extends, but adds a few new properties. Here’s a basic example (that doesn’t actually change the way the object works because it’s setting the properties to the default values):

var myDMC:DesignerMovieClip = new DesignerMovieClip();
addChild(myDMC);
(...)
myDMC.saturation = 1;     // Saturation: 0 (grayscale) -> 1 (normal, default) -> 2+ (highly saturated)
myDMC.contrast = 1;       // Contrast: 0 (grey) -> 1 (normal) -> 2 (high contrast)
myDMC.brightness = 0;     // Brightness offset: -1 (black) -> 0 (normal) -> 1 (full white)
myDMC.exposure = 1;       // Brightness multiplier: 0 (black) -> 1 (normal) -> 2 (super bright)
myDMC.hue = 0;            // Hue offset in degrees: -180 -> 0 (normal) -> 180

To super-saturate an image, you’d do this:

myDMC.saturation = 2;

And obviously, you can tween it with whichever tweening engine you use. In pseudo-code, it looks like this:

SomeTweenEngine.doTween(myDMC, {saturation:2, time:1});

Internally, this class works by adding a ColorMatrixFilter that do all the color changes to the filters list. You can still set the filters manually, however – it concatenates them all before setting the object’s filters. Nothing is overwritten, and you even still access the object’s transform.colorTransform‘s properties. It’s as clean, and safe, of a solution as there can be.

An important disclaimer: this code is heavily based on Mario Klingemann’s Color Matrix class (the same code that’s used in Tweener’s color shortcuts, although the values the properties use are changed). I actually didn’t change much of his code, just stripped down to what I used, maybe changed the way some original values are treated, and then encapsulated in a self-contained display class.

The actual AS3 class is below. It’s simple and could use some improvement, but I guess it works for most cases. It also uses the stage.invalidate() “trick” to avoid re-applying the same thing every time a different display property changes.

package com.zehfernando.display {
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.filters.ColorMatrixFilter;	

	/**
	 * @author Zeh Fernando
	 */
	public class DesignerMovieClip extends MovieClip {

		// Sources:
		//
		// http://www.graficaobscura.com/matrix/index.html
		//
		// And specially Mario Klingemann's ColorMatrix class 2.1:
		// http://www.quasimondo.com/archives/000565.php
		// http://www.quasimondo.com/colormatrix/ColorMatrix.as
		// http://www.quasimondo.com
		// His code is licensed under MIT license:
		// http://www.opensource.org/licenses/mit-license.php

		// Static constants -------------------------------------------------------------------

		// Defines luminance using sRGB luminance
		protected static const LUMINANCE_R:Number = 0.212671;
		protected static const LUMINANCE_G:Number = 0.715160;
		protected static const LUMINANCE_B:Number = 0.072169;

		// Instance propertes -----------------------------------------------------------------

		protected var setToUpdate:Boolean;		// Whether this instance is already set to update on the next Event.RENDER

		// Color properties interface
		protected var _saturation:Number;		// Saturation: 0 (grayscale) -> 1 (normal, default) -> 2+ (highly saturated)
		protected var _contrast:Number;			// Contrast: 0 (grey) -> 1 (normal) -> 2 (high contrast)
		protected var _brightness:Number;		// Brightness offset: -1 (black) -> 0 (normal) -> 1 (full white)
		protected var _exposure:Number;			// Brightness multiplier: 0 (black) -> 1 (normal) -> 2 (super bright)
		protected var _hue:Number;				// Hue offset in degrees: -180 -> 0 (normal) -> 180 

		// Matrices
		protected var saturationMatrix:Array;
		protected var contrastMatrix:Array;
		protected var brightnessMatrix:Array;
		protected var exposureMatrix:Array;
		protected var hueMatrix:Array;

		// Overridden properties
		protected var _filters:Array;

		// ================================================================================================================
		// CONSTRUCTOR ----------------------------------------------------------------------------------------------------

		public function DesignerMovieClip() {
			super();
			setToUpdate = false;
			filters = [];
			saturation = 1;
			contrast = 1;
			brightness = 0;
			exposure = 1;
			hue = 0;
		}

		// ================================================================================================================
		// INTERNAL INSTANCE functions ------------------------------------------------------------------------------------

		protected function updateSaturationMatrix(): void {
			// Create the pre-calculated saturation matrix

			var nc:Number = 1-_saturation;
			var nr:Number = LUMINANCE_R * nc;
			var ng:Number = LUMINANCE_G * nc;
			var nb:Number = LUMINANCE_B * nc;

			saturationMatrix = [
				nr+_saturation,	ng,				nb,				0,	0,
				nr,				ng+_saturation,	nb,				0,	0,
				nr,				ng,				nb+_saturation,	0,	0,
				0,  			0, 				0,  			1,  0
			];

			requestVisualUpdate();
		}

		protected function updateContrastMatrix(): void {
			// Create the pre-calculated contrast matrix

			var co:Number = 128 * (1-_contrast);

			contrastMatrix = [
				_contrast,	0,	0, 	0, 	co,
				0,	_contrast,	0, 	0, 	co,
				0,	0,	_contrast, 	0, 	co,
				0,	0,	0, 	1, 	0
			];

			requestVisualUpdate();
		}

		protected function updateBrightnessMatrix(): void {
			// Create the pre-calculated brightness matrix

			var co:Number = 255 * _brightness;

			brightnessMatrix = [
				1,	0,	0, 	0, 	co,
				0,	1,	0, 	0, 	co,
				0,	0,	1, 	0, 	co,
				0,	0,	0, 	1, 	0
			];

			requestVisualUpdate();
		}

		protected function updateExposureMatrix(): void {
			// Create the pre-calculated exposture matrix

			exposureMatrix = [
				_exposure,	0,	0, 	0, 	0,
				0,	_exposure,	0, 	0, 	0,
				0,	0,	_exposure, 	0, 	0,
				0,	0,	0, 	1, 	0
			];

			requestVisualUpdate();
		}

		protected function updateHueMatrix(): void {
			// Create the pre-calculated hue matrix

			var hAngle:Number = _hue / 180 * Math.PI;
			var hCos:Number = Math.cos(hAngle);
			var hSin:Number = Math.sin(hAngle);

			hueMatrix = [
				((LUMINANCE_R + (hCos * (1 - LUMINANCE_R))) + (hSin * -(LUMINANCE_R))), ((LUMINANCE_G + (hCos * -(LUMINANCE_G))) + (hSin * -(LUMINANCE_G))), ((LUMINANCE_B + (hCos * -(LUMINANCE_B))) + (hSin * (1 - LUMINANCE_B))), 0, 0,
				((LUMINANCE_R + (hCos * -(LUMINANCE_R))) + (hSin * 0.143)), ((LUMINANCE_G + (hCos * (1 - LUMINANCE_G))) + (hSin * 0.14)), ((LUMINANCE_B + (hCos * -(LUMINANCE_B))) + (hSin * -0.283)), 0, 0,
				((LUMINANCE_R + (hCos * -(LUMINANCE_R))) + (hSin * -((1 - LUMINANCE_R)))), ((LUMINANCE_G + (hCos * -(LUMINANCE_G))) + (hSin * LUMINANCE_G)), ((LUMINANCE_B + (hCos * (1 - LUMINANCE_B))) + (hSin * LUMINANCE_B)), 0, 0,
				0, 0, 0, 1, 0
          	 	];

			requestVisualUpdate();
		}

		protected function requestVisualUpdate(): void {
			if (Boolean(stage) && !setToUpdate) {
				setToUpdate = true;
				stage.addEventListener(Event.RENDER, onRender, false, 0, true);
				stage.invalidate();
			}
		}

		protected function doVisualUpdate(): void {
		 	// Create empty maytix
		 	var mtx:Array = [
		 		1,0,0,0,0,
				0,1,0,0,0,
				0,0,1,0,0,
				0,0,0,1,0
			];
			var temp:Array = [];

		 	// Precalculate a single matrix from all matrices by multiplication
		 	// The order the final matrix is calculated can change the way it looks
		 	var matrices:Array = [saturationMatrix, contrastMatrix, brightnessMatrix, exposureMatrix, hueMatrix];

		 	var i:int, j:int, mat:Array;
			var x:int, y:int;

			for (j = 0; j < matrices.length; j++) {
				i = 0;
				mat = matrices[j];
				for (y = 0; y < 4; y++ ) {

					for (x = 0; x < 5; x++ ) {
						temp[ int( i + x) ] =  Number(mat[i  ])      * Number(mtx[x]) +
									   		   Number(mat[int(i+1)]) * Number(mtx[int(x +  5)]) +
									   		   Number(mat[int(i+2)]) * Number(mtx[int(x + 10)]) +
									   		   Number(mat[int(i+3)]) * Number(mtx[int(x + 15)]) +
									   		   (x == 4 ? Number(mat[int(i+4)]) : 0);
					}
					i+=5;
				}
				mtx = temp;
			}

			// Update object filters
			var newFilters:Array = [new ColorMatrixFilter(mtx)];
			super.filters = newFilters.concat(_filters);
		}

		// ================================================================================================================
		// EVENT functions ------------------------------------------------------------------------------------------------

		protected function onRender(e:Event): void {
			stage.removeEventListener(Event.RENDER, onRender);
			setToUpdate = false;

			doVisualUpdate();
		}

		// ================================================================================================================
		// ACCESSOR functions ---------------------------------------------------------------------------------------------

		public function get saturation(): Number {
			return _saturation;
		}
		public function set saturation(__value:Number): void {
			_saturation = __value;
			updateSaturationMatrix();
		}

		public function get contrast(): Number {
			return _contrast;
		}
		public function set contrast(__value:Number): void {
			_contrast = __value;
			updateContrastMatrix();
		}

		public function get brightness(): Number {
			return _brightness;
		}
		public function set brightness(__value:Number): void {
			_brightness = __value;
			updateBrightnessMatrix();
		}

		public function get exposure(): Number {
			return _exposure;
		}
		public function set exposure(__value:Number): void {
			_exposure = __value;
			updateExposureMatrix();
		}

		public function get hue(): Number {
			return _hue;
		}
		public function set hue(__value:Number): void {
			_hue = __value;
			updateHueMatrix();
		}

		override public function get filters(): Array {
			return _filters;
		}
		override public function set filters(__value:Array): void {
			_filters = __value;
			requestVisualUpdate();
		}
	}
}

It's sort of funny that once you start to add functionality to existing objects and to reuse them, making them simpler but more powerful, it starts to resemble the way we used to do stuff in AS1 with prototypes and such - only that now we can use actual types and things like autocompletion.

There are other variations of this kind of class I've used previously, but that's meant for future posts, I guess.