Skip to main content

Bazalt

Bazalt is a Python library for the Vulkan API. Use it to prototype GPU and shader work, and to build graphical applications. A C++23 core does the Vulkan work. The Python layer keeps the setup code short.

Installation

Install bazalt with pip:

pip install bazalt

Bazalt supplies prebuilt wheels for Windows and Linux. To build from source, you need a C++23 compiler (GCC 14+ or MSVC 19.36+, Visual Studio 17.6) and the Vulkan SDK.

Key Features

  • Vulkan Core: Bazalt calls Vulkan for all GPU work. You keep explicit control of the hardware.
  • Shader Compilation: ctx.compile_shader() compiles GLSL vertex, fragment and compute shaders from your code.
  • Compute Pipelines: ctx.compute_pipeline() and cmd.dispatch() run GPU compute. The results go back into NumPy. You need no images.
  • Storage Images: Compute shaders also write images. .storage_image(binding) and set_storage_image() bind a read/write image for imageStore and imageLoad. Use this for post-processing and procedural textures. Bazalt records the barriers and the layout transitions between the dispatch and a later sample.
  • Cubemaps and Texture Arrays: Pass a list of layers to ctx.create_image([...], cube=True) or to ctx.load_image([...6 paths...], cube=True) for a cubemap. Omit cube= for a texture array. One mechanism gives you samplerCube and sampler2DArray. Compute can fill empty layered images, for example a procedural skybox. See examples/14_skybox.
  • Mipmaps: load_image() makes mips for files by default. Pass mipmaps=False to stop this. create_image(array, mipmaps=True) makes mips for a NumPy texture. create_image(w, h, mip_levels=N) allocates an empty chain, and cmd.generate_mipmaps(image) fills it from mip 0. This works across every array layer and every cube face.
  • Automatic Barriers: Bazalt computes the barriers for hazards between resources at record time. Examples: dispatch to dispatch, a compute-written buffer to a vertex fetch, a compute-written image to a sample. Context(auto_barriers=False) gives you manual control through cmd.barrier().
  • Pipeline and Buffer Management: A builder makes graphics pipelines. One function makes every buffer type.
  • Command Buffers: Command recording is explicit. Calls chain, as in cmd.bind_pipeline(p).draw(3). The block with cmd.rendering(target): ends the pass for you.
  • Asynchronous Texture Streaming: ctx.load_image() returns immediately. The decode and the GPU copy run in the background. Anything that samples the image waits for it. ctx.upload_progress gives you the data for a loading bar.
  • Hot Reload: Context(hot_reload=True) watches the shaders you loaded, their #include files, and your images. Bazalt applies edits live: shaders recompile and rebuild their pipelines in place, and images upload again into the same handle. Bazalt logs a typo or a bad file and keeps the last good version on screen. A mistake does not stop the application. See examples/12_hot_reload.
  • Frame Timing and Debug Names: Context(gpu_timing=True) makes renderer.gpu_time_ms report the GPU time of a recent frame. This is opt-in and costs nothing when off. Use t = cmd.timer() and t.stop() to time a slice of a recording, then read t.ms. A timer is a handle, so you need no with block, and it reads back headless. The name= argument and the .name() method label buffers, images and pipelines, so validation messages name the object.
  • Headless Rendering: Draw into an offscreen RenderTarget and read the pixels back as a NumPy array. You need no window and no display.
  • Render-to-Texture, MRT and Shadow Maps: Target attachments are ordinary Image objects in any supported Format. Sample target.color[0], or target.depth on a depth-only target, like any other texture.
  • Multi-Window: One Context drives any number of windows. ctx.begin_frame() opens the frame. Each window calls renderer.acquire() and renderer.present(cmd), and owns its own swapchain. If you resize or close one window, the other windows continue to render. See examples/19_multi_window.
  • GPU Selection: bz.list_devices() lists every card with its name, type, VRAM, API version and per-feature support, before a Context exists. Context(device=...) runs on the card you chose. Omit the argument and bazalt chooses for you.
  • Multi-Context and Multi-GPU: Any number of Contexts can be alive at the same time, on one card or on different cards. You can bake with compute on one GPU and render on the other. other_ctx.create_image(image) moves the pixels across with their format, their layers and their cube property. Every other resource stays with its Context. If you give a resource to the wrong command buffer, you get a ResourceError that names the mistake, not a driver crash. See examples/20_multi_context.
  • Wide Hardware Support: Bazalt needs Vulkan 1.2 and uses 1.3 where the driver has it, so it also runs on older integrated GPUs. You request capabilities by name, never by version or by extension.
  • Separated Layers: Windowing (GLFW), the Vulkan Context (GPU initialization) and render targets stay separate. A window is one render target among others.

Quick Start: Rendering Without a Window

The code below runs with no display attached. You can use it from CI and from tests:

import bazalt as bz

ctx = bz.Context()
target = bz.RenderTarget(ctx, 800, 600, depth=bz.Format.D32F)

pipeline = (ctx.graphics_pipeline()
    .vertex_shader(ctx.compile_shader("triangle.vert", bz.ShaderStage.VERTEX))
    .fragment_shader(ctx.compile_shader("triangle.frag", bz.ShaderStage.FRAGMENT))
    .vertex_format([bz.VertexFormat.FLOAT3, bz.VertexFormat.FLOAT3])
    .build(target))

# Interleaved position (x,y,z) and color (r,g,b)
vbuf = ctx.create_buffer([
     0.0, -0.5, 0.0,   1.0, 0.0, 0.0,
    -0.5,  0.5, 0.0,   0.0, 1.0, 0.0,
     0.5,  0.5, 0.0,   0.0, 0.0, 1.0,
], bz.BufferType.VERTEX, bz.MemoryUsage.STATIC, bz.DataType.FLOAT)

cmd = ctx.create_command_buffer()
cmd.begin()
with cmd.rendering(target, clear_color=[0.1, 0.2, 0.3, 1.0]) as c:
    c.bind_pipeline(pipeline).bind_vertex_buffer(vbuf).draw(3)

ctx.submit(cmd)

pixels = target.read_pixels()   # numpy (600, 800, 4) uint8

Quick Start: GPU Compute

Compute needs no window and no images. Dispatch, then read the storage buffer back as a NumPy array:

import numpy as np
import bazalt as bz

ctx = bz.Context()

# double.comp: values[i] *= 2.0 over a std430 float array, local_size_x = 64
sim = (ctx.compute_pipeline()
    .shader(ctx.compile_shader("double.comp", bz.ShaderStage.COMPUTE))
    .storage_buffer(0)      # no stage argument — compute has exactly one stage
    .build())               # no target — compute has no attachments

data = np.arange(128, dtype=np.float32)
sbuf = ctx.create_buffer(data, bz.BufferType.STORAGE, bz.MemoryUsage.STATIC)

pool = ctx.create_descriptor_pool(max_sets=4, storage_buffers=4)
dset = pool.allocate_set(sim, set=0)
dset.set_buffer(0, sbuf)

cmd = ctx.create_command_buffer()
cmd.begin()
cmd.bind_pipeline(sim).bind_descriptor_set(dset, sim, set=0).dispatch(128 // 64)
ctx.submit(cmd)

assert np.allclose(sbuf.read(np.float32), data * 2)

You can mix compute and rendering in one command buffer. A dispatch writes the vertices and a draw reads them, with no extra code. Bazalt records the barrier between them. See examples/11_particles.

Quick Start: Hot Reload

Add one keyword argument. Bazalt then watches the files it loaded: the shaders, their #include files, and the images. On each save, bazalt recompiles and uploads again:

ctx = bz.Context(logger, hot_reload=True)

vert = ctx.compile_shader("shader.vert", bz.ShaderStage.VERTEX)
frag = ctx.compile_shader("shader.frag", bz.ShaderStage.FRAGMENT)
tex  = ctx.load_image("wall.png")
pipeline = ctx.graphics_pipeline().vertex_shader(vert).fragment_shader(frag)...build(renderer)

while window.is_open():
    bz.poll_events()
    ctx.begin_frame()                  # edits are applied here (and at ctx.submit)
    if renderer.acquire():
        renderer.present(cmd)          # renderer.gpu_time_ms (with gpu_timing=True) gives GPU frame timing

When you edit shader.frag, bazalt rebuilds the pipeline in place. When you save wall.png again with the same size and the same format, bazalt uploads it into the same handle, so the descriptor sets stay correct. A shader typo logs a ShaderError, and the last good pipeline continues to render. A mistake does not stop the application. For the full demo, see examples/12_hot_reload.

Quick Start: Drawing a Triangle

This example starts the window, the Vulkan Context and the SwapchainRenderer. It then compiles the shaders, creates a pipeline and draws a triangle with three colors.

import bazalt as bz

# 1. Initialize Logger and register a callback.
#    Each message carries its severity and source as data, so you can filter
#    without parsing strings. This is optional: a Context created without one
#    reports warnings on stderr by default.
logger = bz.Logger(min_severity=bz.Severity.WARNING)
@logger.on_message
def on_message(msg):
    print(f"[{msg.severity}] {msg.text}")

# 2. Create the window, Vulkan Context, and SwapchainRenderer
window = bz.Window(1024, 720, "Bazalt Demo - Triangle", logger=logger)
ctx = bz.Context(logger)
renderer = bz.SwapchainRenderer(window, ctx)

if __name__ == "__main__":
    # Load and compile shaders. The vertex shader processes our geometry,
    # and the fragment shader determines the final color of the pixels.
    # Shaders are compiled through the Context.
    vert_spv = ctx.compile_shader("triangle.vert", bz.ShaderStage.VERTEX)
    frag_spv = ctx.compile_shader("triangle.frag", bz.ShaderStage.FRAGMENT)

    # The pipeline is a baked state object that tells the GPU how to interpret our data.
    # It is built against a render target, which supplies the color/depth formats.
    # A SwapchainRenderer is one, so is an offscreen RenderTarget — same call.
    pipeline = (ctx.graphics_pipeline()
        .vertex_shader(vert_spv)
        .fragment_shader(frag_spv)
        .vertex_format([bz.VertexFormat.FLOAT3, bz.VertexFormat.FLOAT3]) # Position + Color
        .build(renderer))

    # We interleave Position (x,y,z) and Color (r,g,b) in a single flat array.
    # Vulkan's Normalized Device Coordinates (NDC) range from -1 to 1,
    # where Y points downwards and X points to the right.
    vertices = [
         0.0, -0.5, 0.0,   1.0, 0.0, 0.0, # Top / Red
        -0.5,  0.5, 0.0,   0.0, 1.0, 0.0, # Bottom-Left / Green
         0.5,  0.5, 0.0,   0.0, 0.0, 1.0, # Bottom-Right / Blue
    ]
    
    # Create Vertex Buffer through the Context
    vbuf = ctx.create_buffer(vertices, bz.BufferType.VERTEX, bz.MemoryUsage.STATIC, bz.DataType.FLOAT)
    
    # Create Index Buffer through the Context
    ibuf = ctx.create_buffer([0, 1, 2], bz.BufferType.INDEX, bz.MemoryUsage.STATIC, bz.DataType.UINT32)

    # Command buffers store a sequence of commands for the GPU.
    # For a static triangle, we can record this buffer once during initialization 
    # and submit the same pre-recorded buffer every frame to save CPU time.
    cmd = ctx.create_command_buffer()

    cmd.begin()

    # begin_rendering names the target you are drawing into, clears it, and sets
    # a viewport and scissor covering it. Naming the target is what lets the same
    # code render to a window or to an offscreen image.
    cmd.begin_rendering(renderer, clear_color=[0.1, 0.2, 0.3, 1.0])

    # Bind the baked pipeline and the geometry buffers
    cmd.bind_pipeline(pipeline)
    cmd.bind_vertex_buffer(vbuf)
    cmd.bind_index_buffer(ibuf)

    # Draw 3 indices (1 triangle)
    cmd.draw_indexed(3)
    cmd.end_rendering(renderer)

    # Main game/rendering loop
    while window.is_open():
        bz.poll_events()
        
        # begin_frame opens one logical frame for every window on this
        # Context; acquire() takes this window's swapchain image and returns
        # False when it should sit the frame out (minimized, mid-resize)
        ctx.begin_frame()
        if renderer.acquire():
            renderer.present(cmd)

Metadata

Release files for bazalt 0.15.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for bazalt 0.15.0
File
bazalt-0.15.0-cp313-cp313-win_amd64.whl CPython 3.13 CPython 3.13 Windows x86-64 Details
bazalt-0.15.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.28+ x86-64, Linux glibc 2.27+ x86-64 Details
bazalt-0.15.0-cp312-cp312-win_amd64.whl CPython 3.12 CPython 3.12 Windows x86-64 Details
bazalt-0.15.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 Details
bazalt-0.15.0-cp311-cp311-win_amd64.whl CPython 3.11 CPython 3.11 Windows x86-64 Details
bazalt-0.15.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 Details
bazalt-0.15.0-cp310-cp310-win_amd64.whl CPython 3.10 CPython 3.10 Windows x86-64 Details
bazalt-0.15.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.10 CPython 3.10 Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 Details

Total release size: 28.8 MB

Release files / bazalt-0.15.0-cp313-cp313-win_amd64.whl

Download URL bazalt-0.15.0-cp313-cp313-win_amd64.whl
Size 2.6 MB
Tags CPython 3.13 Windows x86-64
SHA-256 checksum
How to use checksums
46f5e8997435e6e499c15da27a94a2fdcfdb3dc007bd38a966c2da38edb89d21
BLAKE2b-256 checksum
How to use checksums
1e3d049dc25aaedb2b2c7d318dd26176bb03b88a43c8f4c9e87ebb2584154063
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Release files / bazalt-0.15.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL bazalt-0.15.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 4.6 MB
Tags CPython 3.13 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
5267b89c2305c17510e2cf3087a7243688ebccfce51e7b10ccdee8a7323f3284
BLAKE2b-256 checksum
How to use checksums
55ec633c8d08375c88d887e3197e8770276a89eab81d832eb124813933f0b108
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Release files / bazalt-0.15.0-cp312-cp312-win_amd64.whl

Download URL bazalt-0.15.0-cp312-cp312-win_amd64.whl
Size 2.6 MB
Tags CPython 3.12 Windows x86-64
SHA-256 checksum
How to use checksums
24133f24613c3399c2a16d82522628d935c8e417b13e0630c7078dff6b8dee5a
BLAKE2b-256 checksum
How to use checksums
d6fe986a538170dcb782814e78502dcdf541760d42ba9d08d43cdd9c44e23928
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Release files / bazalt-0.15.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL bazalt-0.15.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 4.6 MB
Tags CPython 3.12 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
2b6bbe2766dbbadbb609b4cd114eade675135b6737119135f9bd1ed3eb76855c
BLAKE2b-256 checksum
How to use checksums
b1ad95225dbeb74d39847f65684d00073149d2f04d7f5b9077f8033da36b24a3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Release files / bazalt-0.15.0-cp311-cp311-win_amd64.whl

Download URL bazalt-0.15.0-cp311-cp311-win_amd64.whl
Size 2.6 MB
Tags CPython 3.11 Windows x86-64
SHA-256 checksum
How to use checksums
6cb61b0a1fe36607fe3d86533c92aa9f4042b71e9746240bffbf8376cb5d7246
BLAKE2b-256 checksum
How to use checksums
855f1131f94474bcd526990c859f2e317714a486ae96967b34cbcb6f43f50126
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Release files / bazalt-0.15.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL bazalt-0.15.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 4.6 MB
Tags CPython 3.11 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
4b441d9152e55a41ed9c64659938934cd4eb0b7553cfefac65f32e8dcd9d67ff
BLAKE2b-256 checksum
How to use checksums
f682b3f24a44014a06e6584f60293aad4f0df74afaa68010a5cb61c8f31dc307
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Release files / bazalt-0.15.0-cp310-cp310-win_amd64.whl

Download URL bazalt-0.15.0-cp310-cp310-win_amd64.whl
Size 2.6 MB
Tags CPython 3.10 Windows x86-64
SHA-256 checksum
How to use checksums
54acb8bc04a4d01dc9941ba3da7a0542b099b3355e3748be452b4d1ea08e9513
BLAKE2b-256 checksum
How to use checksums
46bd92f96054546f2e82f34cb3974a24c8ff1feac9a4d06764108d717b47f382
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Release files / bazalt-0.15.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL bazalt-0.15.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 4.6 MB
Tags CPython 3.10 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
f165a1cfe9ae1d04702199ee427071b17e046d1837b8ddd9143978fc19691b49
BLAKE2b-256 checksum
How to use checksums
061f56f9ec85b1e276d18ad10e4efb619bb9cd6ff6a472c3df7cddedbee5a867
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Security for bazalt 0.15.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 8 files in this release have been uploaded with provenance metadata.

Select from 8 files

Attestation for bazalt-0.15.0-cp313-cp313-win_amd64.whl

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.15.0 This release

8 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page