How Adobe Murdered Flash

Flash has been dead for a while. I moved on a long time ago, but back in it's day I was a happy specialist in AS3 development. Flash used to be something special.

In it's heyday Flash had a lot going for it. Unparalleled reach, a solid runtime, a compact file format, great documentation, and most importantly: a thriving community.

And then one day, almost overnight, there wasn't. Within a couple months, everyone was gone. Everyone. Everyone stopped talking about it, even the most enthusiastic Adobe employees stopped blogging about it on their personal blogs. Try to find an AS3 based library project that was still in active development after mid 2012. You won't. It was not a slow death. You'll notice a marked consistency in when they stalled.

HTML5 didn't kill Flash based on merit. That would've taken much longer. It wasn't ready. It still isn't. I cringe when people say this.

Adobe murdered it.


Imagine you're a high-end web dev in 2017 working on some fantastic new experience that uses WebGL and typed arrays, features that have been out for a while. Imagine it all works consistently on 95% of browsers, and the same code compiled natively for mobile. I know it's hard to picture with a web stack, just try.

Suddenly W3C announces whenever you use WebGL and typed arrays together, they demand a 30% royalty on all revenue you make on any app you make involving javascript that uses those features. Hypothetically, there is actually a sane way they can enforce this.

Screw that. Time to start over, get a new set of cross platform tech?

That was Flash in 2012.

In march 2012, Adobe announced "Premium Features for Gaming", a 30% royalty for flash apps that used Stage3D (like WebGL, with better fallbacks), and some optimized memory access instructions. Both features had already been publicly available for months prior to this announcement and people had written games and libraries around them. There were large projects still in development build around them.

3 months later they all-too-quietly reversed the royalty, but by then everyone had quit their AS3 development jobs and moved on to other platforms. Adobe made no effort to publicize the reversal and no one was looking back. We never heard about the recently hyped Unreal Engine 3 support for Flash again. I'd already ported my game projects away. And nobody trusted Adobe as a platform company ever again.

Thanks, Adobe

It's a story worth knowing, because it's a good lesson in how a critical developer community trust violation killed a platform's future overnight.

It was a clear move trying to copy the likes of Apple's App Store and Steam, a ballsy move to monetize their impressive market share. The difference there is those platforms offer a market place, distribution, and visibility in exchange for their cut; they provided a service. It helped that those terms were clear from when from when you would be first considering targeting those platforms.

Adobe had no market place ready. They had no services to offer in exchange. They were simply allowing you to use thin OpenGL bindings and some raw byte arrays, and suddenly expecting a publisher's share while you were still on the hook for any costs of publishing and distributing your app or game.

If they had announced this royalty when they first announced Stage3D it might have been fine. We could have evaluated to use these features with full knowledge of what we were getting in to. It might have been worth it? Or more likely everyone would have avoided these features early on. Instead, they waited until everyone was addicted to using them before surprise announcing their insane cost.

I assume the rug pull wasn't orchestrated in advanced and that this was more of a case of manslaughter or reckless endangerment than first degree homicide. Maybe Flash wasn't profitable enough for the executives behind this stunt to care if it killed the platform or not? Still tragic.

It's funny to consider some of the criticisms about bloat and performance people heaped on it then, considering the state of modern javascript apps. Some of the UX is better these days (linking in flash apps were awful), and maybe someday javascript capabilities will catch up (we can dream), but there are some experiences that I haven't seen replicated since. If anything comes close, it's broken on some browser or devices that Flash covered well. The potential and accessibility were really unparalleled, even now. Terrible web based apps have proven to be just as terrible as terrible Flash apps. Something of value was lost. I would have taken an AIR based app development future over the Electron hell we live in now. Alas.

I'm in a better place now, but wish I could say the same for the web. Whenever I have to do web dev or cross platform development even now, I still shed some tear for Flash. RIP.

Don't Use == null On Unity Objects

Don't use if (obj != null) on a GameObject, Texture2D, Sprite, or any other UnityEngine.Object without understanding when obj == null doesn't mean obj is acctually null.

Instead, always use the implicit bool cast (i.e. if (gameObject)). That way if you happen to use it wrong it will fail to compile instead of give you unexpected values at runtime.

Update: I'm not first to write about this, see this Unity blog post too

Destroyed != null

The problem is a reference to a destroyed object isn't a null reference, even though it pretends to be. Once you destroy a Unity object, even though it's no longer a valid Unity Object, it's still a valid C# object.

UnityEngine.Object overloads the equality operator so that destroyedObject == null returns true. If you're not aware of when that's an issue it can cause you problems.

Playing Dead

So, the weird thing is, that even after a custom MonoBehavior or ScriptableObject has been Destroy'd, any fields or methods you added to the C# class will still work and have valid values (!!!). Your object still works. Just so as long as you don't use any built in externed properties or methods, of course those won't work anymore.

Here's what a destroyed MonoBehavior looks like if you inspect it in the debugger.

Expanded destroyed variable

It even still has the field values I gave it! Also note that if you ToString a destroyed object, it returns "null". The normal string value of a null reference in C# is "", the empty string.

"Hello Object, are you dead?"


Of course accessing the actual MonoBehavior properties throw exceptions, as expected.

Expanded destroyed MonoBehavior properties

Also noticed that you don't get a NullReferenceException as you would if it was actually a null reference, instead you get a MissingReferenceException:


That's how they're able to provide this helpful but misleading hint: It's not a null reference, it's a reference to a gravestone. Ironically their suggestion to "check if it is null" is your first hint that it is in fact, not null.

Yet deadObject == null still returns true in this case, as they've taught you to expect.

That's because Unity overrides the == operator for UnityEngine.Object and all objects that extend it so that == null will be also be true if the relevant native engine object has been destroyed, in addition to if the reference is actually a null reference.

Why do they do this? Because Unity engine objects aren't pure C# objects.

What is a Unity Object?

Unity objects are really just thin C# wrappers for native C++ objects in the native side of the Unity engine that live outside of the managed, garbage collected C# runtime.

When you call Destroy(obj) on a Unity object the C++ engine destroys the native object and nulls out the C# object's internal pointer to it. However, only the garbage collector is allowed to kill managed C# objects, and only when all references to it go out of scope (to prevent use after free errors), so the husk of a C# handle object lives on until it gets garbage collected normally. Of course, any calls to Unity built-in methods or properties on those objects will then fail, because the C++ engine object they reference is gone.


Using the overloaded == and thinking of destroyed objects as null usually works fine. Until you do something like pass a MonoBehavior or GameObject to a method like...

void Asset.IsNotNull(object obj, string message) {  
    if (obj == null) 
        Debug.LogErrorFormat("Assert Null: " + message);

As you might expect now, this will not assert for a destroyed GameObject, even though it's not a valid GameObject reference. Since C# is a static language where the operator implementation used is picked at compile time, the compiler uses the default equality operator implementation for object (that is, System.Object), not UnityEngine.Object's more specialized "check the native pointer too" operator. At compile time, object's basic implementation is the most specific implementation we can safely use for all possible object typed values in obj, and operators aren't runtime polymorphic in C#.

Also remember how my destroyed MonoBehavior still had a valid string reference? References to destroyed objects can cause memory leaks like this. We've had lingering references to dead Unity objects keeping textures, and other large assets from being unloaded from memory when they were otherwise unused, because there was a lingering reference to a destroyed custom MonoBehavior that still referenced it in one of our custom fields.

You never expect this behavior if you think of "destroyed reference" and "null reference" as the same thing.

Stepping Over Gravestones

When you regularly think, as Unity encourages, of a destroyed object as null, that kind of mistake is easy to make. Obviously I've made that mistake enough times to be bitter about it.

Just don't do it. Be aware that Destoyed isn't null. Think of GameObjects and other UnityEngine.Objects like containers, like strings, or Lists. Remember that before you can do operations on those you have to do something like...

if (!string.IsNullOrEmpty(name)) \\...  
// or...
if (list != null && list.Count > 0) \\...  

Honestly, I wish the Unity interfaces didn't hide the fact that destroyed objects are different from null references from you. I wish the canonical way of expressing this was something more standard like:

if (gameObject != null && gameObject.IsValid) \\...  

Which although verbose, makes the behavior clear, and seeing it makes the above gotchas more obvious. This is basically what happens internally in the implementation of these operators too. Unfortunately there's no public property indicating if an object is dead or alive, so we just have to keep aware.

In lieu of that, UnityEngine.Object does implement an implicit conversion to bool, which I always use over equality to null. The benifit of that (besides being shorter) is if (obj) won't compile for regular object references, since most C# objects don't implement that implicit conversion, stopping you from making the mistake with the asset, where you've cast to object somewhere.

So for the Asset, use something like:

void Asset.IsValid(UnityEngine.Object obj, string message) {  
    if (!obj) 
        Debug.LogErrorFormat("Assert Missing: " + message);

And remember to try to diligently null out your object references after you destroy them to prevent unexpected memory leaks, and if you do need to check a Unity object reference for validity before accessing built-in properties, use:

// not != null
if (customMonoBehavior) {  
    customMonoBehavior.enabled = false;
    customMonoBehavior = null;

Stay safe.


I'll write a proper bio later, but for now this is something to establish context for future posts.

I'm Jovanni, I'm a software engineer. I like solving problems, and helping people make neat things. I've worked on GPS tracking suites and assorted web development, but I mostly do full stack development for games. Within that I focus most on client gameplay, architecture, and tooling.

I started out doing school, personal, and contract work in Java and Flash, when ActionScript 2.0 was cool, and then later when ActionScript 3.0 was new JIT darling (and Javascript was still interpreted and slow).

Then I worked at 5th Planet Games for 2 years doing full stack development. Started as an intern, then full time as a lead. Flash client, Java socket severs, MySQL, PHP, web frontend.

After I went as far as I could go there, I went back to University and finished by BS in Computer Science at Cal Poly Pomona.

Sometime during that period, Adobe murdered their excellent and thriving developer community overnight with an idiotic licensing maneuver (which I'll have to write about later). I stopped or migrated development of my remaining Flash projects a while after that.

These days I mostly work in Unity at MunkyFun. Unity's clunky at times, but it does the job well, and the tooling is fantastic. The licencing terms are good too—for now. The docs aren't anywhere near the same class the Flash and AS3 refs were in, and leave a lot to be desired (which gives me an opening). Overall though, the cross platform development story is better than anything else I've worked with, the community is pretty good, and I enjoy working with it.

I'm dabbling in web projects in Go, node, and C#

More to come.

First Post

Finally got around to switching this site to something a little more workable than unmanaged static pages. I finally have a blog!

I went with Ghost because I wanted something simple and Markdown based. Markdown for the same reasons everyone else uses it these days: HTML is ugly, WYSIWYGs lie, and it's what everyone else is using. It's simple, it's light, and because everyone uses it I can change my mind later it'll be less agonizing to switch. Spent an evening to tweak themes, highlight.js, and Disqus for comments; and we're good to go. My portfolio was already originally composed in Markdown anticipating this, and was drop-in easy as hoped and expected.

I also tried some static page generators like Jekyll, and liked them. Decided against them because I'd be giving up the ability to effectively write and edit things with my phone on the crowded bus to work without juggling a file explorer or command line, text editor, and git client (which I do all the time, but would rather not do on my phone). Ghost's admin interface works decently on a smartphone as is. I can more effectively use of those hour long commutes this way.

Enough of that nonsense for now. I didn't make this blog to just talk about setting up blogs. More words to come.