|
| 1 | +--- |
| 2 | +slug: /new-compiler |
| 3 | +hide_table_of_contents: true |
| 4 | +--- |
| 5 | + |
| 6 | +# New Compiler |
| 7 | + |
| 8 | +The compiler is the part of TurboWarp that converts projects to JavaScript. On September 20, 2025, we released a new compiler that is better at analyzing the scripts inside projects to generate even faster JavaScript. |
| 9 | + |
| 10 | +This is the largest single change we've ever made. We've tried to test it thoroughly, but **it's quite likely there are still a few more bugs**. You should report broken projects at [the usual place](https://scratch.mit.edu/users/GarboMuffin/#comments). If your project is broken, you can use https://experiments.turbowarp.org/old-compiler/ instead until we fix the bug in the new compiler. |
| 11 | + |
| 12 | +:::warning |
| 13 | +The new compiler breaks a small handful of custom extensions. See the [extensions](#extensions) section below for details and workarounds. |
| 14 | +::: |
| 15 | + |
| 16 | +The new compiler is currently only available on the website. The packager, desktop app, and other experiment branches will be updated later. |
| 17 | + |
| 18 | +## Performance comparison {#performance} |
| 19 | + |
| 20 | +The performance gain varies a lot depending on the project. Some projects run twice as fast, while others are unchanged, but there should be no projects that run slower. |
| 21 | + |
| 22 | +<table style={{textAlign: "center"}}> |
| 23 | + <thead> |
| 24 | + <tr> |
| 25 | + <th>Test</th> |
| 26 | + <th>Old Compiler</th> |
| 27 | + <th>New Compiler</th> |
| 28 | + </tr> |
| 29 | + </thead> |
| 30 | + <tbody> |
| 31 | + <tr> |
| 32 | + <td> |
| 33 | + <div><a href="https://turbowarp.org/1201938491">Linux in Scratch</a></div> |
| 34 | + <div>Time until shell</div> |
| 35 | + <div>Lower is better</div> |
| 36 | + </td> |
| 37 | + <td>21 seconds</td> |
| 38 | + <td>12 seconds</td> |
| 39 | + </tr> |
| 40 | + <tr> |
| 41 | + <td> |
| 42 | + <div><a href="https://turbowarp.org/611788242">Faster SHA-256 Hash</a></div> |
| 43 | + <div>Hashes per second</div> |
| 44 | + <div>Higher is better</div> |
| 45 | + </td> |
| 46 | + <td>2711 per second</td> |
| 47 | + <td>3010 per second</td> |
| 48 | + </tr> |
| 49 | + <tr> |
| 50 | + <td> |
| 51 | + <div><a href="https://turbowarp.org/310372816">Quicksort</a></div> |
| 52 | + <div>Sort 200000 random items</div> |
| 53 | + <div>Lower is better</div> |
| 54 | + </td> |
| 55 | + <td>0.0515 seconds</td> |
| 56 | + <td>0.0451 seconds</td> |
| 57 | + </tr> |
| 58 | + </tbody> |
| 59 | +</table> |
| 60 | + |
| 61 | +(Tested using Chromium 140, Arch Linux, i7 4790k, warp timer disabled) |
| 62 | + |
| 63 | +## Limitations {#limitations} |
| 64 | + |
| 65 | +### Warp timer {#warp-timer} |
| 66 | + |
| 67 | +The [warp timer](warp-timer) forces blocks marked as "run without screen refresh" to briefly pause after running for 500 ms to prevent an infinite loop from causing you to lose unsaved work. This is why the warp timer is automatically enabled when you open the TurboWarp editor. |
| 68 | + |
| 69 | +Unfortunately, the warp timer breaks many of the assumptions that the new compiler relies on to optimize projects, so do not expect major performance improvements while you are in the editor or otherwise have the warp timer enabled. They won't run slower than they did in the old compiler, though. |
| 70 | + |
| 71 | +### Extension compatibility {#extensions} |
| 72 | + |
| 73 | +All extensions included in the TurboWarp extension list will work the same, and a vast majority of custom extensions will also continue to work the same. |
| 74 | + |
| 75 | +A small handful of custom extensions use an API called `i_will_not_ask_for_help_when_these_break` to integrate more directly with the compiler. We gave this API that crazy name because we knew it was going to break at some point, and we didn't want a small handful of extensions to restrict us from being able to change the compiler's internals when needed. If your project requires these extensions, you can use https://experiments.turbowarp.org/old-compiler/ instead until extensions become compatible. |
| 76 | + |
| 77 | +## Brief technical overview {#technical-overview} |
| 78 | + |
| 79 | +The broad idea is that the compiler analyzes scripts to determine what kinds of values each variable may have at each point in the script. This allows it to remove unnecessary type conversions and generate more specialized code. The old compiler tried to do this too, but was unable to reason about loops or conditionals. |
| 80 | + |
| 81 | +Consider this script that sums integers from 1 to 100. Suppose that the block is marked as "run without screen refresh" and warp timer is disabled. |
| 82 | + |
| 83 | + |
| 84 | + |
| 85 | +The old compiler generates: |
| 86 | + |
| 87 | +```js |
| 88 | +sum.value = 0; |
| 89 | +i.value = 1; |
| 90 | +for (var a0 = 100; a0 >= 0.5; a0--) { |
| 91 | + sum.value = ((+sum.value || 0) + (+i.value || 0)); |
| 92 | + i.value = ((+i.value || 0) + 1); |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +`(+something.value || 0)` is how the compiler converts a variable to a number. In this case it is redundant as the variables are already always numbers, but the old compiler did not realize this. |
| 97 | + |
| 98 | +The new compiler instead generates: |
| 99 | + |
| 100 | +```js |
| 101 | +sum.value = 0; |
| 102 | +i.value = 1; |
| 103 | +for (var a0 = 100; a0 >= 0.5; a0--) { |
| 104 | + sum.value = (sum.value + i.value); |
| 105 | + i.value = (i.value + 1); |
| 106 | +} |
| 107 | +``` |
| 108 | + |
| 109 | +No more unnecessary type conversions. Small changes like this can add up to be significant. |
| 110 | + |
| 111 | +If warp timer is enabled or the script is not marked as "run without screen refresh", the new compiler generates the same code as the old compiler. This is because the repeat block might pause before it finishes all iterations, so other scripts have the opportunity to change variables in unknown ways. For example, if another script changed the "i" variable to a string, `i.value + 1` would act like the "join" block instead of addition. The type conversion would be necessary to ensure the script always works correctly in this case. |
| 112 | + |
| 113 | +(We manually cleaned up the JavaScript for this page. The actual code has no formatting and does not have meaningful variable names.) |
| 114 | + |
| 115 | +## Credits {#credits} |
| 116 | + |
| 117 | +[Tacodiva](https://scratch.mit.edu/users/Tacodiva7729/) did almost all the work and persevered through an impossibly long review process. |
| 118 | + |
| 119 | +Early testers helped us find and fix many bugs before release: |
| 120 | + |
| 121 | + * [Krypto](https://scratch.mit.edu/users/KryptoScratcher/) |
| 122 | + * [Vadik1](https://scratch.mit.edu/users/Vadik1/) |
| 123 | + * [SpinningCube](https://scratch.mit.edu/users/SpinningCube/) |
| 124 | + * [GamingWithDominic](https://scratch.mit.edu/users/GamingWithDominic/) |
| 125 | + * [Scratch_Fakemon](https://scratch.mit.edu/users/Scratch_Fakemon/) |
| 126 | + * [LSPECTRONIZTAR](https://scratch.mit.edu/users/LSPECTRONIZTAR/) |
| 127 | + * [Terminal](https://scratch.mit.edu/users/windowscj/) |
0 commit comments