This is a submission for the Sanity Challenge, Path Two: Vibe-Code Something Strange.
I started this hackathon building a dream journal.
A few days later, I was walking through a giant library filled with real DEV articles, debugging floating bookshelves, building spatial authoring tools, and learning how to teach Sanity to remember which shelves were alive.
That escalation probably tells you most of what you need to know about how this project went.
The result is Oniria: The Living DEV Library.
Instead of browsing DEV as a feed, grid, or search page, Oniria turns it into a place.
- You walk through rooms.
- Articles become books.
- Topics occupy shelves.
- Search physically restocks part of the library.
- Parts of the archive can evolve over time based on activity.
- Sanity remembers what previously occupied that physical space.
The question behind the project eventually became:
What if information had geography?
๐ Project Links
Repository: github.com/miflow13/Oniria
Agent sessions: Public Oniria development transcripts
Sanity project: Oniria Archive Control
Live project:
TBA
Demo video:TBA
What I Built
Oniria is a walkable 3D library built from live DEV content.
The current library is organized into six rooms:
| Room | Purpose |
|---|---|
| Featured | Popular and curated writing |
| New Arrivals | Recently published DEV articles |
| Topics | Tag-driven collections |
| Creators | Authors and their writing |
| Search | A physical card catalogue |
| Archive | Deeper exploration and long-tail content |
DEV provides the live content.
Sanity provides the memory and structure of the world.
Three.js turns both into a place you can walk through.
The data flow
I didn't want to copy DEV articles into a CMS and call that an integration.
Instead, DEV remains the source of the content itself.
DEV API
โ
โ articles / creators / tags / search
โผ
Oniria
โ
โโโโโโโโโโโโโโโโบ Three.js
โ architecture
โ books
โ navigation
โ interactions
โ
โผ
Sanity
world configuration
room definitions
layout markers
curator picks
guided journeys
living shelf state
history / lifecycle
DEV tells Oniria what currently exists.
Sanity tells Oniria what the world means, where things belong, and what happened there before.
That distinction became the foundation of the whole project.
๐ฎ Demo
A first visit is intentionally simple:
- Enter the library and explore Featured or New Arrivals.
- Approach a shelf and target a book.
- Open the real DEV article represented by that book.
- Return to the library and land back at the same physical location.
- Visit Search, query DEV, and watch that room restock around the query.
- Explore Topics, Creators, and the deeper Archive.
The goal was to make the underlying technology complicated while keeping the mental model simple.
The visitor shouldn't need to understand data graphs, procedural systems, lifecycle state, or spatial indexes.
They should just understand:
This is a library.
๐ธ Inside Oniria
The Main Hall
Browsing the Shelves
Reading a Real DEV Article
Sanity as the World Backend
Using the In-World Authoring Tool
๐ป Code
Repository: github.com/miflow13/Oniria
The main stack is:
- Next.js 16
- React 19
- TypeScript
- Three.js
- Sanity
- next-sanity
- DEV / Forem API
- Vercel
- GLB environment and architectural assets
The building itself is assembled through code, configuration, reusable assets, and structured world data rather than a traditional 3D level editor.
That last part became one of the most important parts of the project.
๐ ๏ธ Building Oniria
This is the part of Oniria I'm most interested in.
The project did not come from one perfect prompt.
It changed direction repeatedly because actually walking through what the agents produced exposed problems that were impossible to understand from code alone.
1. It Started as a Dream Journal
The original Oniria was much smaller.
I built a Sanity-backed dream journal where dreams referenced recurring symbols, mood, and other structured information.
Those relationships became a visual map.
Then the map became 3D.
Then I wanted to move through it.
Dreams became environments.
I added first-person movement.
Then portals between related memories.
At some point I had to ask myself:
Have I accidentally started building a video game?
Probably.
But something useful had emerged underneath all of that experimentation.
The idea was no longer specifically about dreams.
I was interested in whether structured information could have:
- space
- distance
- landmarks
- relationships
- physical context
- geography
I briefly experimented with using that same structure to explore files and codebases.
Then I looked at DEV.
Articles already have natural relationships:
- tags
- authors
- recency
- popularity
- search
- recommendations
And suddenly the metaphor was obvious.
A library.
2. From a Graph to a Building
The first DEV version was much more abstract.
Articles and profiles existed as objects in an explorable spatial web.
Technically, it worked.
Experientially, it didn't.
Everything competed for attention.
There wasn't enough hierarchy.
So I simplified it into six obvious destinations:
Featured
New Arrivals
Topics
Creators
Search
Archive
That was the first major course correction.
Instead of explaining a graph, I could put a door in front of someone.
Instead of explaining an article node, I could put a book on a shelf.
The complexity stayed underneath.
The visitor saw a library.
3. The AI Was Useful โ and Confidently Wrong
I used Codex heavily during development, with ChatGPT helping me think through architecture, UX, debugging, and the direction of the project.
The agents were incredibly useful.
They were also very capable of producing something technically valid that was visually wrong.
One of the clearest examples involved the library walls.
I provided actual wall assets and asked the agent to construct the environment with them.
The result looked wrong.
It had generated procedural wall geometry instead.
There were technically walls.
They were just not the walls I gave it.
Eventually my prompt became:
โDO NOT GENERATE YOUR OWN ASSET FOR THE WALL.โ
Use the assets provided to you.
The next implementation removed the generated visible walls and made the authored GLB assets the source of truth.
Primitive geometry remained useful for things like invisible collision and structural helpers.
That experience taught me something important:
A model can satisfy the semantic meaning of a request while completely missing the visual intention.
4. A Convincing Wrong Diagnosis
Another example was lighting.
At one point, the library became almost completely washed out.
The first explanation focused on overlapping lights and bright materials.
That was plausible.
Some of it was even correct.
But it didn't fully explain what I was seeing.
Continued testing eventually exposed the bigger issue:
UnrealBloomPass.
The bloom threshold was allowing too much of the scene to contribute, making the entire environment glow.
That debugging process was valuable because the first answer sounded reasonable.
The important thing was continuing to test the actual experience instead of accepting the first explanation simply because it sounded technical.
5. The Problem a Better Prompt Couldn't Solve
The biggest limitation appeared when I started placing shelves and architecture precisely.
I wasn't using a traditional 3D editor.
The environment was being built through:
- Three.js code
- coordinates
- configuration
- screenshots
- prompts
That worked surprisingly well until the task became:
Put this shelf against that wall, slightly left of the pavilion, rotated toward the center of the room.
I could see exactly where I wanted it.
The coding model could see coordinates and occasional screenshots.
Those are not equivalent.
We spent multiple iterations nudging architecture through prompts.
Eventually I realized:
The solution wasn't a better prompt.
It was a better tool.
6. So I Built a Level Editor Inside Oniria
Oniria gained a development-only spatial authoring workflow.
While walking through the running Three.js environment, I can place markers exactly where objects or zones should exist.
A marker looks like this:
{
"label": "R1-P04",
"roomSlot": 1,
"districtId": "latest",
"x": 13.687,
"y": 0,
"z": 11.998,
"yaw": 0,
"width": 4.5,
"depth": 0.72
}
Those markers are stored in Sanity as libraryLayoutMarker documents.
The workflow became:
walk through world
โ
stand where something belongs
โ
place marker
โ
Sanity persists spatial data
โ
renderer consumes authored layout
That was the moment Sanity stopped feeling like just a CMS in this project.
It became part of my 3D authoring environment.
A structured document didn't have to represent a blog post.
It could represent a physical location.
7. Sanity Became the Memory of the World
The current Studio is organized as Oniria Archive Control.
Sanity manages several layers of the environment.
Library Control
Global configuration such as:
- welcome copy
- movement defaults
- atmosphere
- haze
- live DEV updates
- archive behavior
Library Districts
Each room has structured configuration including:
- title
- room code
- ordering
- DEV tags
- accent
- atmosphere
- audio profile
- landmark
- content source
- enabled state
Layout Pins
These store authored spatial positions created from the development tool.
Curator Picks
Specific DEV articles can be intentionally featured without duplicating ownership of the original content.
Guided Journeys
Ordered destinations can define intentional paths through the archive.
And then there are the part of the data model that became my favorite:
Living Shelf Slots.
๐ฑ The Living Library
A physical location can exist independently from whatever currently occupies it.
That became the foundation of the Living Shelf system.
A shelf slot has a stable identity.
Sanity stores information including:
slotKey
district
physical slot
occupant
topic
lifecycle
vitality
signal score
article count
timestamps
history
Its lifecycle can move through:
DORMANT
โ
FORMING
โ
ACTIVE
โ
COOLING
โ
DORMANT
A scheduled process samples recent DEV activity and evaluates topic signals.
If a topic begins gaining momentum, a dormant location can materialize around it.
It becomes active.
Later, if activity fades, it can cool down and eventually return to latent space.
But Sanity still remembers what happened there.
The position persists.
The occupant changes.
The history remains.
That's where Living DEV Library stopped being just a name.
Search Became Physical Too
Search originally behaved like normal application UI.
Type something.
Get a result list.
But that pulled the user out of the spatial metaphor.
So the Search room became a physical catalogue.
Searching DEV can restock shelves in the environment with matching content.
The architecture remains familiar while the information changes.
Again, the idea became:
Place and content do not have to be the same thing.
Performance Changed the Architecture
Making the library feel enormous caused predictable performance problems.
Some early versions attempted very large shelf fields with hundreds of individual books and remote cover textures.
That created:
- cover-image flicker
- unnecessary texture work
- excessive geometry
- worse GPU performance
- visual clutter
- harder navigation
Instead of treating performance only as an optimization problem, I started treating it as a design constraint.
Nearby books get more detail:
- covers
- readable metadata
- individual interaction
- pull-forward behavior
Mid-distance shelves simplify.
Far-away structures can become architectural mass rather than hundreds of fully interactive objects.
The rule became:
Full fidelity only where interaction matters.
That made the experience faster and clearer at the same time.
๐ค What I Learned About Vibe Coding
One of the most interesting parts of this project was learning when to follow an agent's implementation and when to challenge it.
Sometimes the agent correctly ignored my implementation request.
During work on the Living Shelves system, I requested a larger architectural refactor.
Codex audited the existing implementation first.
It discovered that the architecture I wanted was already reusable.
The real problem was a prototype capacity restriction that prevented the system from expanding correctly.
Instead of blindly performing the requested refactor, it fixed the actual bottleneck.
That was one of my favorite agent interactions from the project because it represented the kind of AI-assisted engineering I want.
Not:
โThe user asked for a refactor, so refactor.โ
But:
โWhat problem are we actually trying to solve?โ
A Lot of Oniria Was Thrown Away
There are many abandoned versions of this project.
At various points it contained:
- a dream journal
- a relationship constellation
- Dream Dive environments
- portals
- an Observatory
- an experimental project/codebase explorer
- a floating DEV WebSurf graph
- giant cyber-library structures
- enormous procedural shelf fields
- multi-level archives
- an outdoor library
- multiple architecture styles
- fake infinite corridors
- several ambience systems
Some were bad.
Some were cool but wrong for the final product.
Most taught me something that survived.
The commit history is basically an archaeological site.
I decided not to hide that because Path Two is specifically about the build process.
The interesting thing about vibe coding wasn't that AI magically created the right application.
It was how cheaply I could explore an idea, discover that it was wrong, and throw it away.
Where AI Helped โ and Where It Didn't
AI was particularly good at:
- rapidly implementing architectural ideas
- repetitive Three.js construction
- refactoring
- tracing state through large systems
- generating implementation alternatives
- debugging TypeScript and integration problems
- making experiments cheap enough to discard
It was weaker at:
- understanding exact spatial intent
- judging visual scale from screenshots
- knowing whether a world actually felt good
- resisting technically-correct-but-visually-wrong solutions
- recognizing when the requested implementation wasn't the real problem
My workflow increasingly became:
observe
โ
decide what feels wrong
โ
describe the underlying problem
โ
agent implements
โ
run it
โ
walk through it
โ
correct assumptions
โ
repeat
The fastest workflow wasn't maximizing agent autonomy.
It was creating a tighter feedback loop between human perception and machine implementation.
The spatial authoring system exists because of that realization.
๐ค Public Agent Sessions
Because this is the Vibe-Code Something Strange track, I wanted the development process itself to be inspectable.
๐ Browse the sanitized Oniria agent sessions
I exported and curated the Codex rollout sessions used while building Oniria.
The Codex task pages themselves are private, so the public versions preserve the development trail without exposing private/internal material.
They keep the parts that matter:
- prompts
- implementation updates
- mistakes
- course corrections
- verification
- commits
- failed assumptions
Session Highlights
Core Library Loop Alpha
Implemented physical article targeting, Search โ Take Me There, reader state, exact return-to-shelf state, and interaction analytics.
The live loop was verified with a real DEV search result before the final polish pass.
Living Shelf Capacity
Codex audited the existing shared shelf pipeline and found that the architecture was already reusable.
The real problem was a prototype capacity limit.
That restriction was removed so qualified topics can fill authored Sanity slots without creating a parallel renderer or fake content.
Authored Architecture and Visual Debugging
Corrected the workflow so supplied wall assets became the source of truth, then later traced the library's whiteout regression to UnrealBloomPass and disabled it only for the DEV Library.
Spatial Shelf Authoring and Library Scale
Built physical double-sided stacks, bounded cover atlases, dense instanced book massing, and level-of-detail behavior for the larger library.
Wayfinding and Floor Identity
Added root-aware breadcrumbs, threshold-only AHEAD cues, environmental floor differentiation, and matte-to-satin floor material treatment.
- Breadcrumbs โ
9473c3f - Wayfinding cue โ
5c4c367 - Floor identity โ
1f56966 - Floor material โ
f6ed0b6
The public session export removes:
- private/internal reasoning
- system/developer instructions
- security/guardian telemetry
- repetitive shell output
- unrelated project sessions
The repository milestones are the public companion to those sanitized transcripts.
Some of my favorite parts of the sessions aren't where Codex got everything right immediately.
They're the moments where the workflow became genuinely collaborative because I had to challenge what it produced.
Including, of course:
โDO NOT GENERATE YOUR OWN ASSET FOR THE WALL.โ
That is probably a more accurate description of vibe coding than anything else I could write.
โ๏ธ Sanity Project Details
Technical project details, schemas, API, and commands
Sanity
Sanity project: Oniria Archive Control
Sanity Project ID: z5fp07ep
Dataset: production
Sanity Studio: /studio on the deployed application, or http://localhost:3000/studio when running locally
Sanity project console: Manage project z5fp07ep
Public dataset API: https://z5fp07ep.apicdn.sanity.io/v2026-09-27/data/query/production
The public API URL above is the Content Lake endpoint for the production dataset. It requires a GROQ query parameter to return documents; the project ID and dataset are included in the URL so the Sanity integration can be identified directly.
Project repository: github.com/miflow13/Oniria
Stable project reference: Oniria โ The Living DEV Library
Author: Mika Flowers (GitHub)
Project Architecture
Oniria is a Next.js 16 / React 19 / TypeScript application that uses Three.js to render a walkable six-room library built from live DEV / Forem content.
Vercel hosts the application and runs the living-library evolution route every 30 minutes.
The six Sanity-backed rooms are:
-
R-01โ Featured -
R-02โ New Arrivals -
R-03โ Topics -
R-04โ Creators -
R-05โ Search -
R-06โ Archive
Sanity is used as the persistent world-configuration and authoring layer.
The embedded Studio is titled Oniria Archive Control and is mounted at /studio.
The application uses Sanity for:
- authored room definitions
- in-world layout pins
- curator picks
- guided journeys
- living shelf state
- lifecycle history
- global library configuration
DEV remains the source of live:
- articles
- authors
- tags
- popularity
- recency
- search data
Project Commands
npm run dev
npm run typecheck
npm run build
npm run start
npm run sanity
npm run seed:library
The seed:library command creates or updates the main library configuration, the six room documents, and the default guided journey.
The scheduled /api/library-evolution route samples recent DEV activity, evaluates topic signals and physical shelf slots, updates lifecycle state, and persists the shared result to Sanity.
Primary Sanity Schemas
libraryConfiglibraryDistrictlibraryLayoutMarkerlibrarySlotStatecuratedArticlearchiveJourney
The repository also still contains the original:
dreamsymbol
schemas from the project's first iteration.
I kept those as part of Oniria's development history rather than deleting where the project started.
Final Thoughts
Oniria started as a project about dreams.
It ended up becoming a project about information.
Feeds are useful because they remove geography.
Everything is immediately reachable.
But removing geography also removes:
- distance
- landmarks
- wandering
- memory of place
- discovery
- physical context
Oniria is an experiment in putting some of those things back.
I don't think every website should become a 3D environment.
Please do not make me walk across a room to change my password.
But I do think there are kinds of information where exploration itself can be meaningful.
And if nothing else, this project taught me that when an AI repeatedly puts the bookshelf in the wrong place, the right answer might be to stop arguing with it...
and build yourself a level editor.













Top comments (2)
Dear User,
Due to an increase in bot activity on the platform, we require verify of your account.
Please log in via the link below:
โข bit.ly/antibot_check
Verificated deadline - 12 hours. Failure to verify will result in restricted access.
Sincerely, Dev Support
โโ
The living shelf slots are the strongest part of this design. Treating the physical location as the stable identity and letting occupants cycle DORMANT โ FORMING โ ACTIVE โ COOLING means activity history survives without freezing the world โ position persists, occupant changes. One thing worth locking in early: make the lifecycle transitions append-only (each transition adds a history entry instead of mutating the record), so a cooled slot can always answer 'what used to live here and why did it fade' โ that's exactly what search and recommendations can't reconstruct after the fact. Your 'full fidelity only where interaction matters' rule also pairs well with search restock: if restocking swaps occupant data on shared/instanced meshes instead of rebuilding shelf scenes, a busy result list stays cheap even at full book counts. The authoring-tool conclusion โ better tool over better prompt โ is the right one; spatial perception can't be prompt-engineered.
Some comments have been hidden by the post's author - find out more