Decode in Python
Two read paths, both first-class: front to back, or index-first. See the feature matrix for what the conformance suite proves today.
pip install fourdgs
The whole scene, front to back
import fourdgs
scene = fourdgs.read("scene.4dgs") # a path, bytes, or a file-like object
state = scene.gaussians.state_at(1.5, scene.header.cutoff)
state["indices"] # which gaussians exist at t = 1.5
state["centers"] # their positions, moved by their own velocity
state["opacity"] # their alpha, faded by their own marginal
This is where decoding ends. state_at returns index arrays plus attributes so a caller can do what
it likes with them; what happens to those numbers afterwards is not the library's concern.
Pass the file's own cutoff rather than relying on the default whenever you have the Scene — the
threshold is the file's, and it is the same constant the encoder derived its velocity grid from.
A Scene carries more than its gaussians: header, quantization (the grids and the error bounds
the file declares), audio_sources, camera, metadata, attachments, statistics,
chunk_index, summary_offsets, summary_crc_ok, skipped_opcodes and truncated.
audio_sources is empty when the scene has no audio.
Each source reconstructs independently on the scene clock:
for source in scene.audio_sources:
source_state = source.state_at(1.5)
# active, local_time, gain, scene-space position and rotation
The player combines that state with its listener pose. HRTF/panning, distance attenuation, occlusion and mixing are deliberately not format logic.
read(..., recover_truncated=True) is the default: a file cut short mid-write yields everything
complete before the cut, with scene.truncated set. Pass max_sh_band to stop short of the file's
full spherical-harmonic degree.
An instant, without reading the file
The indexed path opens a file the way a seeking client does — the Footer, then the index, then only the byte ranges the instant needs.
from fourdgs.indexed_reader import open_indexed, read_chunk
from fourdgs.readable import FileReadable
scene = open_indexed(FileReadable("scene.4dgs"))
scene.header.gaussian_count, scene.header.duration_sec, len(scene.index)
scene.chunks_for_time(2.5) # the entries covering an instant
scene.chunks_for_range(2.0, 4.0)
scene.bytes_for_time(2.5) # what that seek transfers, before asking for it
for entry in scene.chunks_for_time(2.5):
chunk = read_chunk(FileReadable("scene.4dgs"), scene, entry)
read_camera, read_metadata and read_attachments fetch front-matter records the same way.
read_audio_source_descriptors and read_audio_source_state fetch only small source metadata;
read_audio_range(source_id, offset, length) transfers only the requested payload bytes.
read_audio_sources is the explicit convenience call that materializes every payload.
Any transport
Both readers depend on Readable — something that can report its size and read a byte range — and
on nothing else:
class Readable(Protocol):
def size(self) -> int: ...
def read(self, offset: int, length: int) -> bytes: ...
FileReadable and BytesReadable ship with the package. An HTTP range reader is a few lines and
belongs in the consumer.
A runnable version of the indexed path, which reports what each seek costs, is
python/examples/decode_summary.py,
which CI runs so it cannot rot.
Errors
Failures are typed, and the distinction matters: MalformedFile means the bytes are wrong,
UnsupportedVersion and UnsupportedCodec mean the file is legal and this build cannot read it,
TruncatedFile means it was cut short, and BoundViolation means a declared error bound was not
met. ExceedsReaderLimit means the file may be legal but is larger than a ceiling this reader
declares in advance, so the fix is a larger limit rather than different bytes. All derive from
FourdgsError.
From the command line
4dgs info scene.4dgs # summarize the file and what seeking it costs
4dgs validate scene.4dgs # check it against the specification
4dgs decode scene.4dgs -t 2.5
The specification is the normative reference, concepts is the vocabulary, and the implementation notes collect the decode-side lessons that are easy to get wrong.