One level format for every FPS map.

A .blvl is one baked map: triangles, convex collision, lightmaps and probes, spawns, doors, breakables and triggers, in one coordinate system. Bake from nine engines. Load with one loader.

ut4_uptown rendered in the browser viewerut4_uptown, baked from Urban Terror, rendered in your browser

What a bake produces

The priority is a correct physical environment, geometry and collision you can play on, ahead of visual fidelity. Every importer converges on the same file, but not on a lowest common denominator: a map carries the light its own engine baked, in that engine's units, and says which engine it came from.

A server reads sim and nothing else

Convex colliders as planes or hulls, breakables, doors, push and teleport triggers, spawns, hurt volumes and a gametype mask on every piece. The rest of the file is never inflated.

A client copies straight to the GPU

52-byte vertices, u32 indices, batches sorted by material, block-compressed textures with their whole mip chains, and lightmaps as band-1 spherical harmonics a normal map can bend.

Light in the engine's own model

Gamma8 pages for idTech3, radiosity-basis pages for Source, per-channel L1 for Source 2, ambient cubes for Overwatch, plus the sun, fog and post block each engine authored.

Your data survives a rewrite

Extension sections carry a game's own bytes under a vendor prefix. Every tool that rewrites a level keeps them. The content hash names the map on the wire.

Small enough to stream

Sections are independent zstd chunks of at most 4 MiB, inflated in parallel, and the table says where each section ends, so textures decode while the tail of the file is still arriving.

Debuggable back to the source

A .bsym sidecar says which brush, face, patch or prop every collider and triangle came from, with the engine's own flags, and is never needed to play.

Engines it bakes from

You bake from the games you own. No game content ships with the format or with this site.

Quake III Arena, Urban Terror

pk3 · idTech3

Brushes, patches and shader scripts. 8-bit gamma lightmaps kept byte for byte, the ambient plus directed light grid, tcMod and deform animation carried on the material.

Source 1

Counter-Strike: Source, Half-Life 2, Team Fortress 2, legacy CS:GO

Displacements and static props. Bumped lightmaps with their three radiosity-basis pages, switchable light styles, leaf ambient cubes, VRAD sun and linear fog in the engine’s own numbers.

Source 2

Counter-Strike 2, Deadlock

Per-channel L1 lightmaps, light probe volumes, the stationary sun a renderer adds at runtime with its baked shadow mask, gradient fog, Hable tonemap and exposure window.

Valorant

Unreal Engine 4

Lightmaps plus the indirect lighting cache as a band-1 grid, UE4 height fog and film curve, the IgnoreOnlyPawn collision that stops shots but not players.

Overwatch 2

via DataTool

No lightmaps: a six-axis ambient cube grid normalised into its page and scaled back by gain, the evening sun in the game’s own unit, a 32³ grading LUT.

Call of Duty 4, Modern Warfare 2

IW3, IW4 fastfiles

Read straight out of the fastfiles. Gamma-space lightmaps, model lighting grid, exponential fog from a start distance, the film tweak block.

Halo 2

Master Chief Collection caches

Gamma lightmaps the renderer doubles the way the modulate-2x combiner did, instance geometry, the game’s own sky.

Reflex Arena

text brush maps, paks, workshop

Brushes without lightmaps: a light probe grid, a sun, hemispheric sky light from the fog colour, props and materials out of the paks.

Reading a level

The container is a header, a table and zstd-chunked FlatBuffers payloads. The schemas are the definition of every section, so a reader in any language starts from them. The Rust crate is the reference; the TypeScript reader behind this site's viewer was generated from the same schemas with the same pinned flatc.

Rust
let file = blvl::LevelFile::read("ut4_uptown.blvl")?;
let sim = file.sim()?;
let world = blvl_trace::World::from_baked(
    &sim.colliders, &sim.triggers, &sim.teleport_dests);
let hit = blvl_trace::trace_box(&world, half, from, to, skin);
TypeScript
const level = await LevelFile.open(fromBlob(file));
const sim = (await level.read('sim'))!.root;
const geo = (await level.read('geometry'))!.root;
device.queue.writeBuffer(vbo, 0, vertexBytes(geo));
device.queue.writeBuffer(ibo, 0, indexArray(geo));

In the browser

The viewer opens any .blvl you drop on it, straight from disk, and flies through it with a freecam. It draws one flat style for every engine: albedo times the baked light, the sky cube, the sun where the engine left it to the renderer, and the fog curve the map asked for. Toggle the collider and spawn overlays to see what a server sees. It needs WebGPU, which every current desktop browser has.

The demo levels are the EDS training range and two community maps whose authors let them travel. A 500 MB Source 2 bake opens the same way, from your own disk, and never leaves your machine.