Mapping & Hammer

Reduce BSP File Size: What Makes Maps Large and What Helps

Reduce BSP file size: which data takes up the space, how to measure it, and what lightmap scale, HDR, cubemaps, packing and bspzip -compress achieve.

A BSP almost always gets large because of three things: lightmaps, which are stored twice in an HDR compile, packed content in the pakfile, and HDR cubemaps. First measure which block dominates in your map. After that, a higher lightmap scale on large faces, careful packing and small cubemaps help where it counts. Compressing the finished BSP is the last step and does not replace any of that.

What takes up space in a BSP

A BSP consists of data blocks called lumps. Their layout is explained in What is a BSP file?. Only a few of them matter for size:

DataWhere it livesWhat the size depends on
Lightmapslighting lump, with HDR a second one of equal sizelit surface area, lightmap scale, materials with a bump map
Custom contentpakfiletextures, models, sounds
Cubemapspakfile, under materials/maps/number of env_cubemap entities and their "Cubemap Size"
Lighting for prop_staticpakfile, sp_*.vhv filesnumber and detail of the props, only with -StaticPropLighting
Displacementstheir own lumps and lightmapsnumber and power of the surfaces
Collision, geometry, visibilityremaining lumpsusually small

Two of GMod's stock maps show how differently this turns out (1 MB = 1,000,000 bytes):

  • gm_construct is 36.7 MB. Of that, 24.5 MB are lightmaps: 12.3 MB for LDR and exactly the same amount again for HDR. The pakfile takes 3.8 MB, including 2.1 MB of HDR cubemaps and 1.4 MB of prop lighting. Collision data comes to 3.1 MB, and the lumps of the 110 displacements to 0.8 MB in total.
  • gm_flatgrass is 47.4 MB although the map is almost empty. 41.6 MB of that is the pakfile, and 39.1 MB of it sits in just eight HDR cubemaps.

Measure before you cut

At the end of its log, VRAD lists all data areas. In the Memory column, the number before the slash is the size in bytes. The rows that matter look like this (from a small test map):

Object names       Objects/Maxobjs  Memory / Maxmem  Fullness
LDR lightdata         [variable]       36320/0        ( 0.0%)
HDR lightdata         [variable]       36320/0        ( 0.0%)
pakfile               [variable]       24864/0        ( 0.0%)
physics               [variable]        4178/4194304  ( 0.1%)

bspzip shows what is inside the pakfile. The first command only lists the names. The second writes the pakfile out as a ZIP file, which you can sort by size in an archive program:

bspzip -dir mymap.bsp
bspzip -extract mymap.bsp pakfile.zip

For an uploaded BSP, the Map Scanner also shows the file size, the number of pakfile files and an excerpt of them.

Lightmaps: scale and HDR

The lightmap scale states how many units one light sample (luxel) covers. Every luxel takes four bytes, and according to the Valve wiki faces with a bump map store four times as many. If you halve the scale, the number of luxels roughly quadruples. In our test map with two rooms, lighting took about 10 KB at scale 32, about 36 KB at the default of 16 and about 564 KB at scale 4.

  • Raise the scale to 32 or 64 on large, evenly lit faces. The price is softer, less precise shadow edges.
  • Only lower it where a shadow really has to be sharp.
  • Apply tools/toolsnodraw to faces nobody sees. VBSP removes them, and their lightmap goes with them.
  • In Hammer, the 3D view mode "3D Lightmap Grid" shows the luxel density of every face.

The second big lever is HDR. VRAD knows -ldr, -hdr and -both. The help text of GMod's VRAD does not list the three parameters, but the program accepts them. Without any of them it only calculates LDR. With -both, VRAD writes a second lighting lump of the same size, with -StaticPropLighting an additional second .vhv file per prop, and you build the cubemaps twice. An LDR-only map is therefore considerably smaller, but gives up HDR for all players. The trade-off is described in the Source Engine lighting guide.

Pakfile: content and cubemaps

Only pack what the map uses, and nothing every player already has with the game. Textures almost always account for the largest share. Resolution, format and checking the list are covered in packing custom content into a BSP.

Cubemaps are underestimated. buildcubemaps writes one texture per env_cubemap into the pakfile, and for an HDR map an additional .hdr.vtf. The HDR version stores 16 bits per channel and, in the two stock maps, is about sixteen times the size of the compressed LDR version: about 77 KB at 32x32, but 4.9 MB at 256x256. Every step in the "Cubemap Size" field quadruples the file. So leave the size on "Default" and only raise it for individual spots with large reflective surfaces. More on cubemaps in fixing cubemap reflections.

Keep away from bspzip -deletecubemaps on a packed map: according to the Valve wiki, the command deletes all VTF files in the pakfile and not just the cubemaps.

Compressing the BSP: bspzip -repack -compress

bspzip can repack a finished BSP and compress it with LZMA in the process. The file remains a .bsp. According to the Valve wiki, GMod's engine loads such maps, and GMod's changelog from March 2025 explicitly mentions LZMA decompression for BSP files.

The catch is the tool. GMod's bspzip names the option in its help text, but in our test (build of 3 September 2026, 32 and 64 bit) it exited on -repack -compress with exit code 1, without any output, and left the file unchanged. -repack on its own worked. For GMod, the Valve wiki therefore points to the bspzip from Team Fortress 2. We used that to compress copies of the two stock maps:

MapOriginalLZMA-compressed BSPOriginal as .bz2
gm_construct36.7 MB10.1 MB12.4 MB
gm_flatgrass47.4 MB12.6 MB13.9 MB

We did not check in game whether GMod loads exactly these files without errors. So test a compressed map yourself on client and server. The Valve wiki also names three restrictions, and they determine the order:

  1. Pack all content first. bspzip will not pack anything into a compressed BSP, so you would have to decompress it again with -repack without -compress.
  2. Then build the cubemaps for LDR and HDR. On a compressed map, buildcubemaps crashes.
  3. Compress last and check the sounds. For packed sounds in ADPCM format the wiki documents playback errors, tested there in Team Fortress 2 and Counter-Strike: Source.

Why size matters for servers

Every player needs the BSP locally before joining. With FastDL the client first asks for a .bz2 version, and with the Workshop the addon is compressed during upload. The size on disk is therefore not the download size. The individual paths are covered in FastDL or Workshop.

The table above shows what follows from that: the LZMA-compressed BSP is only 9 to 19 percent smaller than the .bz2 of the original, and additionally wrapping a compressed BSP as .bz2 changed the size by less than one percent in our test. One of the two files even grew slightly. Compression therefore mainly saves disk space on the server and on the player's machine, plus download time where no .bz2 is available. What you save on lightmaps, cubemaps and textures, on the other hand, pays off on every path and also reduces what the game has to load into memory.

FAQ

Why is my map suddenly much larger after buildcubemaps?

The cubemaps are written into the pakfile as textures, in two versions for an HDR map. High "Cubemap Size" values and many env_cubemap entities quickly add up to tens of megabytes. Check the sizes with bspzip -extract.

Does a smaller BSP make the map faster in game?

Not directly. The frame rate mainly depends on how much the engine draws per frame. Coarser lightmaps and smaller textures do save memory and loading time, though. What determines the frame rate is covered in optimizing GMod map performance.

Is there a maximum file size for a BSP?

We cannot confirm a fixed upper limit for the whole file in GMod. What is limited are individual data areas, see Source map limits, and the direct download from the game server. In practice, the players' waiting time sets the limit.

Can I still change a compressed BSP later?

Yes, but not directly. Decompress it with bspzip -repack without -compress, make the change and compress again. A new compile produces a fresh, uncompressed BSP anyway.