New Improvements to Streaming Enabled

Hi Creators,

We are excited to share that we have made improvements to Streaming Enabled giving developers a new Model.StreamingMode to make streaming easier to implement. Some of these updates have been requested by the community for a long time and we’re excited to see what you create using them!

New Property: Model.StreamingMode

  • Atomic - Models stream in or out as a unit
  • Non-atomic - Models stream before descendants
  • Default - Same as non-atomic (we are exploring ways to optimize this for the future)
  • Persistent - Always streamed
  • PersistentPerPlayer - Always streamed for specified players

New Functions:

  • Model:AddPersistentPlayer(Player)
  • Model:RemovePersistentPlayer(Player)

New Event: Workspace.PersistentLoaded(Player)

  • Fires when all persistent models are loaded for the player

Note: the properties and functions are server-side only and should not be used in local scripts. Changing the streaming mode on a client via a local script or client module script is not supported. The PersistentLoaded even will fire on server and client, but it will normally fire before the player is loaded.

Model Streaming Behavior Differences

In current streaming, models under the workspace that don’t have part ancestors are always sent during loading. As a result, if those models have part descendants those models may exist on the client with partial descendants or no descendants at all until the parts are streamed to the client.

With atomic models, the model and all its descendants are sent as soon as any descendant of the model is required due to streaming. This typically happens when a part descendant of the model is close enough to a client’s replication focus to be streamed. Atomic models can be sent due to other descendants being streamed, such as a joint being needed for other streamed parts. When an atomic model is sent to a client, the model will not be parented into the workspace until all the model descendants have been received.

Stream out is also handled differently for atomic models. With nonatomic models, only parts and their descendants are subject to stream out. For atomic models, when all descendants of the model are safe to stream out and stream out is occurring due to memory pressure or opportunistic stream out, the model itself will be streamed out on the client.

Currently, Default means the same as Nonatomic, but we expect this to change in the future. We will give developers the option to opt into that change when it happens, and advance notice will be given.

Once the PersistentLoaded signal has fired persistent models always exist on clients and are never streamed out. Similar to atomic models, Persistent models are added under their parent when they are complete. A lobby your experience uses frequently is a good example of something you may want to persist.

PersistentPerPlayer models behave the same as atomic models, except for those players that have been added using AddPersistentPlayer. For those players, the model will behave as a persistent model.

Streaming Relative to Experience Loading

Traditional nonatomic streaming models that are not descendants of parts will always be sent during loading. However, this behavior is changing with the addition of atomic, persistent, and persistent per-player models;

An atomic model with part descendants will be streamed when any of its part descendants are needed by the client.

An atomic model with no part descendants will not be sent during load. Persistent models are also not sent during game loading, but soon after as part of streaming. The PersistentLoaded event should be used to determine when persistent and part-less atomic models have been sent to clients. PersistentPerPlayer models behave like atomic models, except for specified players where they behave the same as persistent models.


Fewer Unnecessary Models on Clients

Traditional streaming sends all models to all clients unless a model is a descendant of a part. This means that models that may not have their descendant parts are always sent during join, and are present on all clients. This is particularly a concern when it comes to player models, as the models and their descendant animations, scripts, etc. are present on all clients even when the player is far away and possibly streamed out.

Video: Models and non-parts exist even for streamed out NPCs

With atomic player models, the model and its descendants are streamed in and out as needed. Notice in the following video that when avatars that have been set to atomic models are streamed out, the entire model is removed.

Video: Models are streamed out for streamed out NPCs

Fewer WaitForChild Calls

With existing, nonatomic models, there is no guarantee that all descendants of the model will be streamed at the same time. This means that if a local script needs to access descendants of the model then multiple calls to [WaitForChild]( may be necessary. For example, if an experience has the following model:

A local script that needs to access all descendants of the model might need to use multiple calls to WaitForChild:

local Workspace = game:GetService("Workspace")
local Model = Workspace.Model
local Part1 = Model:WaitForChild("Part1")
local Part2 = Model:WaitForChild("Part2")
local Sound = Part2:WaitForChild("Sound")

However, if the model were atomic then a single WaitForChild would be sufficient:

local Workspace = game:GetService("Workspace")
local Model = Workspace:WaitForChild("Model")


  • Persistence is meant to be used in rare circumstances, such as when a small number of parts must always be present on clients for local script use. If possible server scripts should be used, or local scripts should be written to be tolerant of parts being streamed in and out.

    • Persistence is not intended to circumvent Streaming, such as to improve visual quality or to avoid using level of detail. Overusing persistence can have negative impacts on the performance of your experience and cause issues on low memory clients.
  • Avoid making models containing very large numbers of instances atomic as this can cause bursty networking transmission and impact player experience.

  • Note that in the unusual case that your script adds a subtree to an already replicated atomic model the subtree will not appear atomically on clients. Subsequent stream in or out of the model will function atomically as expected.

Known Issues (last updated on 08/15/2023):

We are aware of the following issues, and are working on fixes:

  • There is a known issue with animations with player/NPC character models that have been set to atomic. If a character is playing an animation and is streamed out and back in, then the previously played animation may not be applied. [Fixed 8/8/2023]

  • Character models for players that have been streamed out may not be fully removed from memory. We are working on a fix for this, but developers should use caution when setting player models to atomic. [Fixed 2/23/2023]

  • Models with ModelStreamingBehavior == Persistent will not be sent until a character or replication focus exists. Fix is coming soon. Workaround is to manually set a replication focus if automatic character loading is disabled. [Fixed 3/1/2023]

  • Models with ModelStreamingBehavior != Default may not replicate correctly when stored outside of Workspace (ReplicatedStorage, ETC). Fix is coming soon. Workaround is to not set the streaming model until the model is under the Workspace. [Fixed 3/1/2023]

  • Calling ModelInstance:RemovePersistentPlayer (or AddPersistentPlayer) for a player that is in the process of being removed, such as via the CharacterRemoved callback can result in a crash. Fix coming soon, until available please avoid calling ModelInstance:RemovePersistentPlayer (or AddPersistentPlayer) in such situations. [Fixed 3/1/2023]

We understand that we threw a lot at you in this post so if you have any questions or concerns, please comment below! We will be happy to help.

For additional information on streaming enabled please see: Content Streaming

If you encounter a bug related to this API and are able to file bug reports please create a separate bug report for better visibility.

Thank you.


This topic was automatically opened after 10 minutes.

This update is such a W that its one of the only announcement posts I liked. Streaming control like this has been LOOOOOOOONG overdue in the engine.

now all we need is require(id) disabling but thats for a different post


I’m crying
This is beautiful.



We’ve been circumventing streaming with the old Camera trick to keep our game’s roadways streamed in at all times to avoid instances of players falling through the world and avoid using the pause feature. This’ll help us get rid of a lot of hacky workspace hierarchy practices.

One thing I’d love to see is the ability to hard-set the radius at which a model streams out. I could do a lot of memory optimization by forcing key models to stream themselves out at custom radii that fit my game’s needs. Example use case here would be forcing denser props like traffic signals to stream out at a tighter radius than the rest of my environment.


There it is. The update that will finally make me use StreamingEnabled for everything moving forward.

Excited to see how the Persistent Per Player property/function will play out, otherwise this is a cry that’s finally been answered.

Godspeed, Roblox. Now, off I go rewriting everything related to my Level of Detail systems :REAL:


Roblox should stop creating “Default” options for new features. Any developer that leaves their game on a Default setting is at risk for their game breaking if the default ever changes. Instead, the setting should simply be set to the default when a developer creates a new place - after that the setting should stay the same and thus keep the same functionality until the developer chooses to change it.

Besides that, I’m very glad we finally have some finer control over what gets streamed in or out.


This is awesome!

I never used Streaming because things that I wanted loaded weren’t and things that I didn’t want loaded were. Glad to see we’re getting somewhere with Streaming, and I’m hyped to use it.

Also, is there a way to enable these icons?


I am now going to use StreamingEnabled in my own games. That sounds absurd, but it’s happening. This fixes my main issues with StreamingEnabled, wow. Actual per-player model support.

I don’t think a few developers understand: this is basically an entirely new feature. This means you can stream entire areas to specific players, without any hackiness. Using officially supported Roblox features. That’s crazy! Literally never ticking StreamingEnabled off again lol. This is going to be a bit of a curve, but it’s a welcomed one.


Don’t believe those icons are part of the update but I would love to have the option to see what will get loaded in and what wouldn’t with a toggle on the explorer.


Alright, I’m sold on using StreamingEnabled for the majority of my experiences going forward. I am deeply thankful for the team delivering a long-requested update.

I might still have to consider a custom solution for some of my more niche use cases (don’t worry about following up on this, I know what I’m doing) but otherwise I’ll be far more inclined to use streaming to improve the overall world experience for my players.

unrelated but hopefully replication control is the next thing we can have more control over ykyk


This sounds great, but like it was said by the OP, this is a lot of info to throw at us at once. I’ve had very good luck with streaming enabled, so this looks like I’ll be able to refine memory/bandwidth/CPU usage in even more fine detail than before. I love stuff that can be optimized to death. :sweat_smile:


Due to the recent change where localscripts can execute almost anywhere including parented to a model in workspace by changing the RunContext property.

How would that work for models with streaming mode set to atomic?

Does the local script execute everytime the model is streamed in or is it executed once and reused?

Side Question: Are there any plans to change the default ModelStreamingMode for characters?


We’re so excited about this for Jailbreak. Jailbreak is an old project with a lot of code. Previously we’d have to modify a ton of code to support something like StreamingEnabled. Now we have a shot. Thank you for these features!


Extremely awesome, looking forward to using this as I’ve had some trouble with my client loading architecture + streaming.

It would be really cool to see something like the atomic streaming mode for simple replication also, or else some way to tell when a hierarchy subtree is fully replicated, there’s a lot of cases with e.g. UI on the client where sometimes a tree of instances won’t be replicated in its entirety before the local script starts to run, leading to some bizarre workarounds and really long WaitForChild chains. It would be cool if I could easily have that local script wait on an entire subtree being replicated.


So persistent models and part-less atomic models are effectively the same? (Are persistent models streamed in atomically, i.e. we don’t need to wait for descendants once we know the model has replicated?)


Yes and yes. They are effectively the same and all persistent models are atomic.


This is a massive improvement to Streaming Enabled, removing all of the roadblocks that I’ve had with it in the past. Huge step in the right direction, kudos to the team working on this. Looking forward to adapting my games to use it!


If you make the root of the subtree an atomic model then you just need to wait for the model.


Outstanding update that many of us have been waiting a long time for.

I do have a detail-specific question though: the documentation makes it very clear that Persistent models are intended to be used extremely sparingly, beyond what one would normally expect for a warning for such a thing given that the norm was streaming disabled for a long time.

Why is this warning so dire, for lack of a better word? I of course understand that setting a model as Persistent means it loses the benefits of streaming, but do Persistent models incur additional serious performance loss or something? Is this not the same as if it were a model in a non-StreamingEnabled game?