Stop proliferation of levelbuilder sourceUrls in level animation JSON#67763
Merged
Conversation
breville
approved these changes
Aug 18, 2025
breville
left a comment
Member
There was a problem hiding this comment.
Thanks very much for investigating and beginning to address this long-standing issue! Looking forward to the follow-up work as well.
This was referenced Aug 20, 2025
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Last week, @breville shared an issue of a level on
testfailing to load animations. This coincided withlevelbuilderbeing down. Our p5 levels (Game Lab, Sprite Lab) support a custom animation JSON field. This level, and many others have animations with a levelbuilder URL in theirsourceUrl, which causes an undesirable dependency. For example, the level at https://test-studio.code.org/courses/coursee-2019/units/1/lessons/9/levels/6?noautoplay=true&no_redirect=true depends upon the image at https://levelbuilder-studio.code.org/api/v1/animation-library/wzaShBj3E3JC6ynAQTv56K3MIRlpF19i/category_characters/kid_outline.pngBefore fixing existing levels with this problem, this branch aims to prevent it from happening more. I looked at the two ways that levelbuilders are known to author this JSON. I also added some validation to explicitly prevent a level from being saved if it contains a levelbuilder URL in its animation JSON.
Extra Links Option
The older of the two methods for authoring animation JSON is also the most flexible. Using the link, the author can view the full animation JSON for any level or project simply by visiting it. They can then copy and paste it into other levels. This is available on all environments to users with sufficient privileges.
Currently, this pop-up shows absolute URLs. If you add animation from the library to a project on levelbuilder, you might see something like:
If this gets pasted into a level file which is loaded on another environment, we wrap it our media proxy to make sure it can be loaded without a CORS violation. At that point you might see something like:
The relative URL we should actually show in both of these cases is just
/api/v1/animation-library/spritelab/9j7wtr58woBSBVOZqrWQ66Msi.JqL6vF/category_people/kid_outline.pngHowever, this is only safe because
/api/v1/animation-library/tells us it's a library animation. These relative links should be available on any environment (prod/test/levelbuilder/localhost), and because they're relative, the lab won't need to proxy them.If the animation is not from the library, we still need the full absolute URL. This includes images at images.code.org and curriculum.code.org (seldom used, but supported), or studio links that include
v3/animationsin the path.The
v3/animationspath is for animations that were created (drawn or uploaded) or modified on the environment itself, ie. using the Piskel editor in Game Lab or Sprite Lab. A relative path will only work on the environment where the image exists in this case, so we need to save the full URL. This does imply the undesirable dependency still exists, but the only way around that is to update the levels themselves. This is follow-up work.Changes:
Proxy unwrap
If we detect that the URL includes our proxy prefix, we'll split the URL and decode it, displaying the original URL.
Preserving relative URLs for animation library assets
If the path indicates it's a library asset, we'll split it at the domain (e.g. levelbuilder-studio.code.org or studio.code.org) and just return the later part.
Absolute URLs for non-library assets
If it's not a library asset, we guarantee a properly formatter URL based on the current environment. This is the pre-existing anchor tag logic and is only used when
props.sourceUrldoesn't exist and we end up with a raw relative URL. This is the path we'd take for animations that uploaded directly into the project, for example.Screenshots
The screenshots compare the JSON for two animations, one created by the user and one from the library (a bear).

Before (Production)
The user animation has a full production link with version. The bear animation url is also absolute and includes a proxied version of the animation's actual source URL.

Before (Levelbuilder)
Same as above, except the absolute URL for includes levelbuilder-studio.

After (Localhost)
The user animation still has a full URL, out of necessity. The bear animation has a simple relative URL for the un-proxied original source. If added to a level, this will work equally well on any environment without creating a cross-environment dependency.

Lesson Edit Page
The lesson edit page includes a JSON field for specify animations. Even with the changes above, it's possible that we will still need to show a levelbuilder URL - if the user created the image on the levelbuilder environment. It's also possible to type anything into this field, so it makes sense that we should validate against the URLs we want to prevent.
We were already validating for malformed JSON so it was straight-forward to add a check for source URLs that include
https://levelbuilder-studio.code.orgAsset Management tools
Levelbuilder has a set of tools available (at
/sprites) which allow authors to upload/replace animations, create new JSON blobs for levels, and to update Sprite Lab's default costumes and backgrounds. Fortunately, the relevant tool ("Generate Animation JSON for a level"/sprites/select_start_animations) only provides relative URLs already. No functional changes were needed here but I did take the opportunity to add some nicer formatting to the output text.Before:

After:

@katiejofr supported these changes:
Links
Testing story
I had to update one test case to reflect that
withAbsoluteSourceUrlsno longer returns proxied URLs for absolute URLs. This function is only used for the pop-up that provides authors with the JSON to copy. It doesn't change any actual app functionality.Deployment strategy
Follow-up work
Audit and update all p5 levels that currently have a levelbuilder dependency.
Privacy
Security
Caching
PR Creation Checklist: