I had never used Blender before starting this project. I knew what it was, of course, but if you had asked me to create a room, place paintings on the walls, configure materials and lights, add cameras and export everything as a GLB file, I would not have known where to start.
I have been exploring AI agents for a while, especially the idea of using an LLM not only to generate text, but to operate existing tools and create real artifacts. What if I could describe an exhibition in natural language and use AI to help me turn it into a museum that people can actually visit?

The project starts with a local export of my Notion database about nineteenth-century mythological painting (I like this kind of things). It contains artwork metadata, images and my own notes. Claude on AWS Bedrock researches that collection and proposes an exhibition. Python validates the proposal and translates it into a deterministic build specification. Blender creates the rooms, paintings, lights and cameras, and exports the result. Finally, a Three.js application turns the GLB into an interactive museum in the browser.
The interesting part for me is not the final 3D room. The interesting part is the path from an idea expressed in natural language to a real .blend file created by a tool I had never used.
Two different AI layers
There are actually two AI stories in this project.
The first one happened while building it. I used an AI coding agent as a collaborator to explore Blender’s Python API, create geometry, work with materials, configure lighting, inspect GLB metadata and iterate over the result. I was still defining the architecture and deciding what the system should do, but I did not need to become a Blender expert before producing something with Blender.
The second AI layer lives inside the application. Claude acts as a curator. It researches a bounded collection of paintings, chooses a narrative, decides which works belong together, organizes one or two rooms and defines the visitor route.
These two layers are related, but they are not the same thing. One helped me build the tool. The other is part of the tool’s runtime.
In both cases the useful pattern is the same: AI handles intent and exploration; deterministic software owns the dangerous and boring details.
The architecture
The complete flow looks like this:
I deliberately split the workflow into stages. Asking an LLM to create a museum in one giant step would be easy to demo and difficult to trust. Here every stage leaves an inspectable artifact behind: the imported catalog, the proposal, the accepted plan, the Blender build specification, the GLB, the content bundle and the final manifest.
If something is strange in the museum, I can see where it became strange.
AI decides what. Code decides how.
The most important decision in the project was not to ask the LLM to generate arbitrary Blender Python code.
That approach would look impressive in a short demo, but it would make validation almost impossible. A model could invent an API, place a painting outside the room, create overlapping frames, forget a camera or return code that only works with a particular Blender version.
Instead, the model returns a MuseumPlan. It is a declarative contract describing curatorial intent:
class MuseumPlan(BaseModel):
schema_version: Literal["1.0"] = "1.0"
plan_version: int
title: str
thesis: str
request: ExhibitionRequest
entrance_room_id: str
rooms: list[RoomPlan]
connections: list[RoomConnection]
route: list[RouteStop]
A room contains dimensions, atmosphere and artwork placements. A placement contains a wall, a normalized position, a display width, a center height and a lighting intention. The plan says that a painting belongs on the east wall at a certain position. It does not say which Blender mesh operations are required to put it there.

That distinction is the boundary:
The AI decides what exhibition to create. Deterministic code decides how to create valid Blender geometry.
Pydantic checks the shape of the response. Additional validators check identities, room topology, route continuity, image availability, painting dimensions, corner clearances, door clearances and frame overlaps. The LLM response is treated as untrusted JSON, even when it comes from a model that has just been given the schema.
If the response is invalid, the application can send the validation issues back to the model for a bounded repair attempt. If it remains invalid, nothing is published.
Using Blender as a build engine
Blender runs as an external application in background mode. Python prepares a non-executable JSON build specification and starts Blender with a small entrypoint:
return [
executable,
"--factory-startup",
"--background",
"--python",
str(entrypoint),
"--",
"--build-spec",
str(build_spec),
"--output",
str(output),
]
Inside Blender, the bpy runtime resets the default scene, creates the architecture, builds the frames, loads the artwork textures, configures room and focal lights, adds the visitor camera and attaches semantic metadata to the objects.
def build_complete_scene(spec, build_spec_path):
reset_complete_scene()
themes = create_exhibition_themes(spec)
materials = create_museum_palette(spec, themes)
collections, connection = create_complete_architecture(spec, materials)
create_complete_scenography(spec, collections, connection, materials, themes)
for room in spec["rooms"]:
add_room_fill(room, collections[room["id"]])
for painting in spec["paintings"]:
add_complete_painting(...)
add_complete_spot(...)
add_visitor_spawn(...)
The scene is validated from inside Blender before it is exported. Expected object names, counts, artwork identities, route markers, colliders and geometry constraints are checked against the build specification. Only then does the pipeline save the .blend source and export the browser-ready .glb.
The build also happens in a temporary staging directory. The final output is replaced only after every artifact and hash has been verified. A failed Blender process cannot leave half a museum in the publication directory.
This is more machinery than a weekend 3D room normally needs. It is also the part that turns the experiment from “the model produced something once” into a repeatable software pipeline.
The curator does not see everything
The local catalog contains 270 records, with 190 eligible nineteenth-century works. Sending the complete database and every image to an LLM would be expensive and unnecessary.
Discovery is divided into two steps. First, Claude receives a compact projection of the eligible catalog and selects a bounded candidate set. Then it receives detailed metadata and reviews only for those candidates and creates the exhibition proposal.
The model never receives image bytes, local paths, import diagnostics or the complete Notion export. It must select existing stable artwork IDs, and the resulting plan is checked against the real local catalog.
The proposal is not accepted automatically either. Discovery creates an immutable review artifact. I can inspect it, ask for a refined alternative and explicitly accept only the version I want. Acceptance creates a draft; building and publishing are separate operations.
This separation matters because generation, acceptance and execution are different decisions. An LLM response should not become published state just because it parsed correctly.
From Blender to the browser
I did not want to bypass Blender by recreating the museum directly with Three.js. Blender is the authoring and build tool; Three.js is the visitor runtime.
The GLB contains more than visible meshes. Paintings carry stable artwork IDs, room IDs and route order. Walls and floors carry collision metadata. The scene contains route stops, label anchors, lights and a visitor spawn camera. The web application reads those semantics instead of guessing them from object positions or filenames.
Before enabling the Enter museum button, the browser verifies the exhibition manifest, content, build report and GLB identities. Once loaded, the visitor can walk through the rooms, follow the curatorial route, inspect each artwork, read the complete review and switch between Spanish and English content.
The application is static. There is no backend, database, AWS credential, Blender process or LLM call in the browser. Each published exhibition is a self-contained bundle that can be hosted with ordinary static files.
This is a PoC, not a general 3D authoring platform.
Rooms are rectangular. Exhibitions contain one or two rooms and up to 24 artworks. The scenography is generated from a small set of themes. Blender operations are intentionally limited, and the visual result is obviously not the work of an experienced 3D artist.
The source catalog is also very specific: nineteenth-century mythological painting, exported from my own Notion database. A different collection would probably expose different assumptions in the curator and the spatial rules.
But the experiment is not really about museums. A museum is only a good excuse to test a broader pattern:
The professional tool could be Blender, a DAW, a CAD application, a video editor or a spreadsheet engine. The model does not need to replace the tool. It can provide a new interface to it.
What I learned without learning Blender
I still would not describe myself as a Blender user. If you open Blender and ask me to model this museum manually, I will probably get lost in the interface.
But I now understand enough of its concepts to work with it as an automation platform: scenes, collections, meshes, materials, lights, cameras, custom properties, headless rendering and GLB export. More importantly, I have a repeatable system that creates something I could not have created before.
AI did not make the engineering disappear. In fact, most of the project is about contracts, validation, boundaries, deterministic builds and failure handling. What AI changed was the entry cost. I could start with the result I wanted and explore an unfamiliar tool from there, instead of spending weeks learning the interface before knowing whether the idea was worth building.
That is the part I find interesting. AI is not only a faster way to write the software we already know how to write. It can be a bridge into tools and domains that were previously outside our reach.
Demo: https://gonzalo123.github.io/museum
Full source code is available in my GitHub.






























