Performance
The sea is built to hold a high frame rate on ordinary clients. This page explains what costs what, so you can spend where it shows.
How it is drawn
- The surface near the camera is a small set of skinned
EditableMeshtiles. Each vertex hangs on a bone, and the module moves bones every update; moving a bone is far cheaper than rewriting the mesh. - The camera sits at the corner of a 32-cell block, so only four tiles are ever animated, and the mesh never has to move when you walk: a root bone carries the re-centring.
- Past the animated ring, one static mesh with a flat skirt runs out to
RenderDistance. - Foam is a fine plain mesh over the inner area with a soft edge; the film is one translucent skinned mesh. Both are optional layers.
- The wave function is Gerstner math with fixed-size arrays and no allocation in the hot loop.
Everything updates at UpdateRate (60 per second by default), not at the render rate, so a 240 fps client does no more mesh work than a 60 fps one.
What to turn
| Want | Turn |
|---|---|
| The biggest single saving | Quality = Fast (fewer animated vertices, no film, foam every fourth update) |
| Fewer updates per second | UpdateRate (30 is fine for slow seas) |
| Smaller animated area | InnerCells down, or CellSize up |
| No foam | FoamStyle = None |
| No film | Highlights off |
| Flat lighting | Lit off draws the water unlit |
| A still sea | StaticWaves builds the surface once and stops the clock; FlatSea removes the waves entirely |
Ocean presets set sensible values for their look; Island (the default) is the balanced one.
Streaming
When streaming loads a new area, Roblox rebuilds every skinned mesh it finds. The module keeps its meshes small and few so that rebuild stays at a few tens of milliseconds rather than a stall.
EditableMesh access
The wave mesh is an EditableMesh, which an experience has to allow in its Experience Settings. Without it the module never builds the mesh: the sea is flat at SeaLevel for visuals and physics alike (the flat water stands in as the surface), and the Output says so once. Ocean.Waves.MeshAvailable() reports which case you are in.
Adaptive quality
Studio has a generous EditableMesh memory budget; phones and low-end PCs do not, so a sea that builds in Studio can be refused in the live game. The renderer handles that on its own with a ladder of rungs:
| Rung | What is drawn |
|---|---|
| full | Everything the settings ask for: four tiles, the horizon mesh, foam, film |
| no film | The surface film is dropped |
| no foam | Foam (and its animated image) is dropped too |
| one tile | A single tile for the whole lattice, fewer bones |
| half lattice | One tile with half the cells per side |
| quarter lattice | One tile with a quarter of the cells per side |
| flat | No EditableMesh at all: a flat slab in the water's colour |
- If a build is refused, the sea drops to flat at once, so there is always water on screen, and this client's own queries (swimming, the underwater tint) follow the flat surface.
- It then climbs from the bottom, one rung at a time, every few seconds. A refused climb keeps the current rung and doubles the wait before the next try, up to three minutes.
- If a mesh update costs more than 7 ms for three seconds, it steps down a rung and holds off climbing for a while.
So each device settles on the best rung it can hold and keeps probing upward slowly. The rung in use is the Level attribute on the OceanSurface model, and a warning in the Output says when a build was refused. Renderer.SetLevel(n) (0 to 6) forces a rung for testing; the sea climbs back from there.
To give weak devices an easier start, set Quality to Fast and keep FoamStyle off Animated: that removes the film and the foam image before the ladder has to.
Physics side
Heights, buoyancy and swimming use the waves the mesh can draw. A wave shorter than three of the finest cells (CellSize times the quality's scale) cannot be shown, and fades in up to six; the physics applies the same fade, so things float on the water you see. With large cells (say CellSize 32 on Fast) only the long swell remains, in the mesh and in the physics alike.
Buoyancy samples the surface at up to eight points per floating part every physics step; hundreds of floating parts are fine, thousands are not. Water events are checked once per tracked instance per frame. Obstacles, dry regions and zones are re-read twice a second, so tagging a moving ship as an obstacle costs nothing extra.
Measuring
The Performance section shows live stats while a preview or playtest runs: frames per second, milliseconds per mesh update, animated and moved vertex counts. The OceanSurface model (under Terrain at runtime, under the Camera in a preview) carries the same numbers as attributes: UpdateMs, Animated, Bones, Tiles, Hitches, WorstMs.