Ruffle Logo

Stable Releases, API, Text... and Everything Else!

Dinnerbone

•

2026-11-04

Well, we said we'd make these posts more often... it's been over two years since the last one! We'll come up with a poor excuse for our absence at the bottom of the post, but we're hoping that all these really cool changes will distract you from that!

As always, let's summarize with some fancy numbers before we get into any of that.

Since the last blog post (over 2 years ago)...

  • ActionScript 3 API has gone up from 76% to 82%!
  • ActionScript 1/2 Language has gone from 95% to 99%!
  • ActionScript 1/2 API has gone up from 78% to 82%!
  • We've merged 2,329 pull requests from 92 people users! (humans? bots? who's to say anymore!)
  • We've closed 3,379 issues!
  • We've added 1,922 new automated tests!
  • The number of languages on Crowdin has gone from 25 to 37.

Stable releases! Multiple of them!

A frequent request over the past few years has been to make less frequent, stable, releases - and thanks to the work of @kjarosh to finish up all the loose ends, we're finally there.

In fact, we're now on version 0.7.1 at the time of writing this! We've had quite a few stable releases since the first one back in May to prove out the system, so it's about time we actually announced them!

Stable releases do not have a fixed frequency, and will mainly drop when we feel that enough has changed to warrant a new version, and that we're feeling reasonably confident in the stability and performance.

We'll still release nightlies for those brave souls who are willing to test things ahead of time and want to help us find and fix bugs, but we do recommend that users switch to stable releases if they, frankly, just don't want the fuss.

Desktop App

Our desktop app does not auto update by itself, so you'll still need to manually download either the stable or nightly releases whenever you wish to update. You can find the links on our download page. For Linux users, our Flatpak app updates automatically for stable releases. We also now provide Linux ARM64 and i686 builds!

Browser Extension

We've changed the official store releases of our extensions to use stable releases, nice and easy. If you want stable releases, there's nothing to do here, hurray! If you do want to live on the bleeding edge, you'll have to manually download and install the nightly version of the extension. They do not self update, so we do not generally recommend this outside of testing.

Web (Self-Hosted)

The self-hosted version of Ruffle is available in both stable and nightly versions on our download page, as normal.

Web (CDN/UNPKG)

If you installed Ruffle onto your website through our recommended <script src="https://unpkg.com/@ruffle-rs/ruffle"></script> tag, you're now using stable releases. Hurray! If you want to stay on the bleeding edge, though, swap that to <script src="https://unpkg.com/@ruffle-rs/ruffle@nightly"></script> instead.

Android App

The Android app is technically a separate project and is not affected by the stable releases. If there's demand for this, we'll look into it - but we also want help maintaining it!

Speaking of apps, @madsmtm has been working on an iOS app! It's still a work in progress but worth checking out.

A stable JavaScript API

In early versions of Ruffle, we just started putting methods and properties on the <ruffle-player> element itself. Turns out, this wasn't so smart, and we ended up conflicting with the ExternalInterface API and Flash's own (mostly undocumented) API.

To fix this, we've added a .ruffle() API that we recommend using instead. This mostly means using foo.ruffle().load() instead of foo.load(), but the full API can be found on our JS docs. This API is versioned; if we ever need a v2, we can .ruffle(2). The previous API still exists for compatibility, but we do not recommend its use for newer code.

AVM1 (ActionScript 1/2)

You may have noticed that whilst our "ActionScript 1/2 Language" percentage has crept its way up to 99%... We're pretty proud of that! Unfortunately, we're very much in the "90/90 rule" area.

The first 90 percent of the code accounts for the first 90 percent of the development time. The remaining 10 percent of the code accounts for the other 90 percent of the development time.

We've made many incorrect assumptions during the early days of our AVM1 implementation which we're trying to correct now in the final 1%. We can confidently say that almost every real world movie will work, but we're constantly finding areas that we would not match Flash, and trying to fix those is difficult.

The most notable fix here comes from @relrelb in #24725, which finally fixes the long standing issue in Super Mario 63 where the game is stuck on the final battle.

Other notable changes include:

  • @jarca0123 fixed the clip removal logic when rewinding, fixing bugs in Electricman 2, Ratman Ralph, Super Sub Hero and Pink Tac Toe (#21336).
  • @CUB3D fixed the base clip path of functions, fixing a bug in Champion Archer (#20861).
  • @ste42 fixed timeline scope on target clip removing, fixing a bug in Achilles (#20837).
  • @kjarosh fixed a crash when swapping children, reported in many games such as Ben 10: Ben to the Rescue (#19594).
  • @kjarosh implemented loadVariables for non-movie-clip targets (#19467).
  • @kjarosh fixed a crash when removing objects (#19462).
  • @Toad06 fixed the unloading of button's children, fixing looping sound effects in Escaping the Prison and a regression in Pestilence Z (#18520).
  • @dinnerbone introduced an AVM1 "playerglobal", laying the groundwork for moving our built-in functions to ActionScript (#23730).
  • @relrelb moved Point and AsBroadcaster from Rust to ActionScript (#24873, #24736).
  • @moulins made a bunch of built-in classes resolve dynamically (#24327, #24500, #24656, #24658, #24696, #24961, #25052.
  • @moulins migrated functions and super to NativeObject, finally killing off TObject (#19936, #20204).

A screenshot of Super Sub Hero A screenshot of Ben 10: To The Rescue

AVM2 (ActionScript 3)

We've come a very long way since the early days when we had the infamous "ActionScript 3 is not yet supported" screen. You've been able to play almost any game in Ruffle with little issues for a while now, which is super cool!

However, as we've been focusing heavily on "getting it working correctly", we certainly are not as fast as Flash Player was, it's true. Our philosophy has always been to make things correct, add all the tests we can possibly think of, and then use those tests to make sure that any optimizations we do are correct and stable.

All that said - the fantastic @Lord-McSweeney has been hard at work improving the performance of AVM2 content in Ruffle, almost all of which also bring some bonus bug fixes:

  • Preallocate stack sizes and improved the verifier/optimizer (#17140).
  • Rewrote the optimizer with proper state merging (#19884).
  • Added peephole passes to the optimizer (#20202).
  • Fewer allocations on method calls (#19946).
  • Faster native calls (built in methods) (#20328).
  • Vector indexing improvements, which fixed one version of Run 2 (#20565).
  • Use stack slices for activations, speeding up stack access (#20829).
  • Removed PrimitiveObject (#18995).
  • Better trait verification, fixing a black screen in Cardinal Quest (#17119).

We've also been working on general fixes and improvements, for example:

  • @Lord-McSweeney made Dictionary match Flash's "JIT mode" behaviour for QName keys, fixing a black screen in Typing Maniac (#24890).
  • @Lord-McSweeney fixed the serialization of Dictionary, fixing save games in Reactance 2 and Muse Quest (#21428).
  • @Lord-McSweeney fixed the version of some Context3D methods, fixing Tamagotchi Friends: Dream Town, Zombidle and Mutant Fighting Cup 2 (#22077).
  • @evilpie implemented the missing RegExp features, fixing Castle Wars 2 (#20426).
  • @adrian17 made ByteArray.compress more accurate to how Flash compresses things, fixing progress not being saved in GemCraft Labyrinth (#18366).
  • @uaua made hover events dispatch whilst dragging (#21661).
  • @atirna made mouse events be clipped to an object's scrollRect, fixing the inventory cursor in The Enchanted Cave 2 (#24475).
  • @SuchAFuriousDeath fixed a crash in Array.splice, which fixed Dynapuff Jump (#23197).
  • @AllinolCP made URLRequest with an empty body perform a GET, matching Flash (#20242).
  • @adrian17 fixed a crash with using null ratios in Graphics's gradient APIs (#18699).
  • @evilpie implemented BitmapData.histogram, fixing BoxHead: The Nightmare (#18075).

A screenshot of Typing Maniac A screenshot of Zombidle A screenshot of The Enchanted Cave 2

Text Fields & Fonts

A frequent topic on these blog posts, it turns out that text rendering is hard... Who knew!

  • @kjarosh has put an incredible amount of work into improving our font rendering; both in terms of correctness and performance:
    • Implemented AVM2 TextField.styleSheet (CSS-like) support, as used by Transformice and Dead Maze (#19143).
    • Fixed text layout measurements (#18332).
    • Fixed text alignment for autosize fields, fixing misaligned text in Nanaca Crash and Rogue Soul 2 (#18494).
    • Improved the performance of rendering fonts by measuring them less (#23862).
    • Made autosize bounds calculate lazily (#18870).
    • Implemented glyph fallback for device fonts, matching Flash Player (#20639).
    • Added support for device font rendering on the web via an offscreen canvas (#22254).
    • Added a FreeType device font renderer on desktop, using the OS to rasterize glyphs (Linux for now) (#24418).
    • Fixed rendering device fonts with small shears (#22684).
    • Improved the x/y scaling of device fonts (#22209).
    • Implemented the TextField AVM2 scroll event (#24625).
  • @cmdocmd added HTML <img> tag parsing/serialization (#24802).
  • @wsxarcher made it possible to load ttf/otf/ttc/otc files as default fonts in the extension (#17668).
  • @adrian17 implemented a better text wrapping algorithm that supports CJK and parentheses (#22730).

A screenshot of Rogue Soul 2 A screenshot of Transformice

Audio & Video

We've had many bugs with sounds, especially older AVM1 games. Thankfully, we're significantly better than we were 2 years ago – no more sudden loud noises here!

Rendering Improvements

It's hard to overstate how many improvements have been made to our rendering pipeline, but the biggest change of all has to be that we now finally support hairline and scaled strokes. Thanks to the hard work of @darktohka in #23011, many games and movies now look true to how they did in Flash, and not just "crazy thick lines" all over the place. Hurrah!

Club Penguin before/after

Other notable improvements include:

  • @ChrisCPI added JPEG XR image loading (#23789).
  • @kjarosh added support for transparency in masks, fixing rendering in Typographist and Tim Credible (#21497).
  • @dinnerbone has started rewriting the wgpu backend for performance (#24947).
  • @kjarosh fixed using masks in BitmapData.draw, fixing rendering in Fireboy & Watergirl 2 and 3 (#21624).
  • @Aaron1011 implemented surfaceSelector in Context3D.setRenderToTexture (#15495).
  • @SuchAFuriousDeath implemented Context3D.setStencilReferenceValue & Context3D.setStencilActions (#20875).
  • @cookie-s partially implemented Matrix3D (#18888).
  • @1bugproof fixed copyPixels degrading opaque pixels, which made it impossible to progress past level 4 of Toxic 2 (#24492).
  • @n-raine fixed a crash with out-of-bounds pixel copying (notably in Earn to Die and Earn to Die 2: Exodus) (#23898).
  • @Bubby4j fixed an OOM by flushing GPU work periodically during cache processing (#23175).
  • @crumblingstatue fixed a crash when doing a colorTransform out of bounds, as seen in Mega Mash (#20363).
  • @kjarosh made sure we flushed any draws before updating bitmap textures, fixing overlapping text in Robot Wants Kitty (#19066).
  • @cookie-s partially implemented DisplayObject.z, making games like Hexagon playable (#19405).
  • @kjarosh added a workaround for using blendmodes in masks, fixing many crashes such as in FlashThemes' content (#23452).

A screenshot of Fireboy & Watergirl 2 A screenshot of Tim Credible

Desktop Application

The desktop application has been getting a little love, with a focus on better RTL and CJK support. We're not entirely done yet, the IME support is still a work in progress, but it's certainly getting there!

  • @kjarosh added the ability to export the currently playing content as a Ruffle Bundle (#21994).
  • @kjarosh added basic IME support on desktop, which is currently disabled by default pending further improvements (#19666).
  • @kjarosh enabled LTO for releases (#23863).
  • @kjarosh added gamemode support on Linux (#17940).
  • @kjarosh added a workaround for RTL scripts in UI (#20265).
  • @kjarosh fixed missing characters in many languages (e.g. Japanese) (#20263).
  • @kjarosh coalesced mouse move events on desktop for performance, fixing freezes in Club Penguin's Puffle Rescue (#24881).
  • @SuchAFuriousDeath fixed a crash when saving files on macOS (#22776).

Export Bundle dialog

Web & Extension

Ruffle is used on so many websites now, including the Wayback Machine (does that count as us being on every website?). We've also surpassed over 1,000,000 active users on our Chrome extension alone, which is absolutely astounding!

  • @robinmiau added a "background tick mode" so content keeps running in hidden tabs (#23395).
  • @robinmiau improved the context menu behaviour and styling (#23535).
  • @evilpie added support for mapping gamepad input on the web (#18083).
  • @kjarosh added RTL support for context menus (#19950, #19943).
  • @danielhjacobs made the extension use content scripts on all browsers except old Firefox (#20255).
  • @kjarosh fixed CORS issues with content trying to download from fpdownload.adobe.com (#22168).
  • @kjarosh added URL rewriting to help preserve content that relies on broken or outdated links (#18252).
  • @kjarosh made scrolling the mouse not scroll the page when it scrolls AVM2 content (#19029).

General Bug Fixes

Don't let the lack of specificity fool you, these fixes are just as important as all of the above, perhaps even more so – it's just hard to group them together! ;)

  • @Lord-McSweeney allowed running AVM2 SWFs with SWF versions 8 and below (#21055).
  • @Lord-McSweeney fixed a crash with SWF v8 code moving AVM1Movies around, fixing SpongeBob Decorating Dilemma freezing on its loading screen (#21528).
  • @Lord-McSweeney fixed a crash with AVM1 movies loaded by AVM2 trying to overwrite themselves, fixing crashes in many games such as Bloons Super Monkey 2 and the Sara's Cooking Class series (#19761).
  • @Lord-McSweeney made root MovieClips register as a character, fixing many games, such as Mister Rain (#21514).
  • @nivkner fixed the ordering of running frame scripts, fixing a bug with Flight (#19941).
  • @nivkner made queued goto actions overwrite prior gotos, fixing a bug with Epic Battle Fantasy 4 (#20254).
  • @nivkner made frame scripts refresh registration, fixing a bug with Snail Bob (#20293).
  • @Lord-McSweeney fixed a queued goto bug with SWF9 (#22408).
  • @Lord-McSweeney fixed queued play/stop actions whilst queueing gotos, fixing Gacha World, Tomb Chess and William and Sly (#24684).
  • @lsaa fixed invalid gotoAndStops in SWF 9 & SWF 10, fixing the medal graphics in Super Crazy Guitar Maniac Deluxe 2 (#24741).
  • @dinnerbone fixed an ImportAssets bug by pausing preloading until imports finish (#16968).
  • @moulins fixed an ImportAssets bug with executing DoInitAction code with the proper context, fixing Cubis 2 (#21944).
  • @cookie-s fixed hitTest on the edges of shapes, fixing characters falling through floors in Castle Cat and Powerfox (#20811).
  • @masterwebx fixed BitmapData.hitTest with 0 alpha threshold, fixing collision issues in Super Smash Flash 2 (#23870).
  • @kjarosh unified key input across all platforms, moving Flash-specific behaviour into core and simplifying the frontends (#19616).
  • @kjarosh fixed a bug when placing an object at an already occupied depth (#20749).
  • @CUB3D fixed many crashes with SharedObject (#18046).
  • @kjarosh fixed a crash loading LZMA-compressed SWFs with incorrect lengths, fixing Sherwood Dungeon not loading (#24487).
  • @Bale001 allowed playing of frames beyond what the SWF header defined (#21526).
  • @Lord-McSweeney corrected the order that objects on goto'd frames are made, fixing games such as Road of the Dead or Wedding Trouble (#22677).

Epic Battle Fantasy 4 A screenshot of Castle Cat 4

Test Framework

We're extremely proud of our tests in Ruffle, with over 500 unit tests and over 4500 conformance tests (often consisting of dozens or hundreds of smaller tests). That's a 60% increase in tests since our last blog post! We're firm believers that any investment into test frameworks will always be worth it and pay off, and in a world where more people are throwing automation at problems, tests help keep the slop at bay.

Since the last blog post, we've had over 170 PRs aimed directly at our tests, excluding all the regular PRs that happen to include tests. Here are some notable improvements:

Where have the blog posts been?

Okay, I'll drop the royal "we" and take responsibility here. I kept pushing off a blog post because of time, energy, and fatigue. I've been working on Ruffle for 7 years now. I love this project, I'm extremely proud of everything we've accomplished, but it's hard for me to stay dedicated for that long. I've gone through my share of burnout, it sucks, and so whenever I feel myself getting close I take a step back and treat myself to a long break. I've taken a fair few breaks since the last blog post, and other amazing contributors and maintainers have stepped up to keep things running, but the blog post kept waiting for me to come back and finish. Every time I thought about it, it was a bigger and more daunting task, and it became something of a vicious cycle - putting off the scary thing because it's been put off and made scarier. A tale as old as humanity!

I already anticipate some "Why not let AI write it for you?" in some discord thread or comment section, so I will address that up-front: Frankly, I'm no friend of LLMs. I especially do not believe it has a place writing articles and blog posts. If you wanted just the facts, ask your friendly local LLM to do that - it might even be correct. If you want what we, the maintainers, think and feel - you need a human to write that.

It's 2026, are we AI yet?

This isn't a section I anticipated writing when I wrote the last blog post, but the future sure is weird.

The rise of LLMs has certainly been a thing, and I'm sure you don't need yet another blog post prophesying doom or raining blind praise. However, I do think it's fair to talk about how LLMs have affected us as a project, especially with the "decompile everything" trend that is happening.

Being completely honest - it's not great, but it's not the end of the world either. We've had some genuinely useful and helpful contributions aided by LLMs, and I do believe that at some tasks, it is becoming okay, with the right guidance and someone to shepherd it and take responsibility for its actions. However, with every useful change we get a dozen not-so-useful ones, and with a project that values correctness above all else, it's extremely hard to trust a tool that hallucinates behaviours.

We have largely adopted the LLVM AI Tool Use Policy (with a few project-specific rules), which mostly boils down to "a human must be in the loop". For the correctness of our project, for the quality of our code, and for the sanity of our maintainers - we need people to be able to understand, and take responsibility for, the output of their tools. It's hard to imagine projects thriving where that isn't true.

It's worth noting that our PR backlog is long, and it rises every day. It's very easy to create a dozen pull requests, but each one requires more effort and time from our maintainers than it did for someone to create them. Maintainer burnout is real, and it's becoming a serious problem. It's not solely due to LLMs, of course, but it's certainly the biggest factor as of writing this post. This is a problem that affects the entire open source community as a whole, not just Ruffle, and there's no solution in sight. Be kind to your local OSS maintainers! :)

Thank you all!

I'm so very proud of how far we've got with Ruffle, and I know that it only gets better from here. Thank you to everybody who helped keep Ruffle alive throughout these crazy times, and see you in the next post!

As a reminder, Ruffle is a completely open source project and we're always looking for contributions! Some quick ways to help out:

  • Speak another language? Help translate Ruffle to your native language over on Crowdin.
  • Got a few bucks to spare? Sponsor the project and help us fund future development.
  • Like playing games? Try Ruffle on your favourite content and report any issues you find!
  • Can you code? Whip out your favourite IDE and help us improve - the player is in Rust, everything web is TypeScript, and the Android app is Kotlin.
  • Got a friend? Tell them to help out! :D

Thank you all again, and see you in the next blog post! <3