Updated 3D Environment for Library
Reading DEV Articles
A lot of you wonderful people enjoyed my last post on Oni, so heres an update on what I've been working on.
I've been spending the last few days building the DEV Library inside Oniria, and somewhere along the way the project stopped feeling like "a cool Three.js scene" and started feeling like I was building my own tiny game-engine workflow.
The idea behind the library is pretty simple:
DEV articles become physical books.
Instead of opening DEV.to and scrolling through cards, you walk through rooms, browse shelves, pull out an article, read it, and return to the exact place you found it.
But actually constructing that space turned out to be one of the most interesting problems I've hit so far.
Earlier versions of Oniria leaned much harder into a cyber-world aesthetic: glowing structures, highways, towering architecture, and DEV articles scattered through something closer to a futuristic city. It looked interesting, but the more I built it, the more I realized the environment wasn't helping me understand the information inside it.
Articles were technically spatial, but the space itself didn't explain why anything was where it was. Eventually I started asking a simpler question: if thousands of articles were going to become physical objects, what kind of place would naturally organize them? The answer was obvious — a library. That shift changed the project from decorating a cyber-world with content into designing an actual information architecture: rooms became categories, shelves became collections, books became articles, and the building itself could communicate how DEV is organized.
That decision also created a completely different engineering problem. I was no longer placing a handful of futuristic landmarks; I was trying to build an entire navigable library — rooms, shelves, paths, furniture, signs, lighting, and hundreds of precise spatial relationships — almost entirely through Three.js code, without a traditional live level editor.
That progression looked like this:
cyber city
↓
looked cool, but lacked informational meaning
↓
"what spatial metaphor actually fits articles?"
↓
library
↓
rooms = information architecture
↓
now we need to physically build it
↓
wait... we don't have a level editor
↓
build our own tools
The problem: I wasn't using a level editor
Before building the in-world authoring tools, most object placement happened directly in Three.js. All by code. No 3D Editor. Ha, take that, purist!
I wrote a small helper that clones one of the loaded GLB assets, then applies its world position, scale, and rotation:
const placeAsset = (
template: THREE.Group,
x: number,
y: number,
z: number,
scale = 1,
rotationY = 0,
rotationX = 0,
) => {
const instance = template.clone(true)
instance.position.x += x
instance.position.y += y
instance.position.z += z
instance.scale.multiplyScalar(scale)
instance.rotation.y += rotationY
instance.rotation.x += rotationX
group.add(instance)
return instance
}
That made placing something fairly straightforward:
const table = placeAsset(
readingTable,
-16.2, // x
0.05, // y
-31.8, // z
1, // scale
Math.PI / 2,
)
But moving it still meant editing numbers:
table.position.x += 0.8
table.position.z -= 1.2
table.rotation.y += Math.PI / 4
Or changing the original call:
const table = placeAsset(
readingTable,
-15.4,
0.05,
-33.0,
1,
Math.PI * 0.75,
)
Then:
save
↓
reload
↓
walk back across the library
↓
look at it
↓
"nope"
↓
change the numbers again
That was basically my level editor. 😭 Below is an actual snippet of what I was working with:
And once the environment grew beyond a handful of props, it became painfully obvious that this wasn't going to scale.
With a normal interface, I can change some CSS, refresh the browser, and immediately see whether a card needs another 16px of margin.
In a 3D world, changing:
position: [-16.2, 0, -31.8]
might mean walking across an entire room just to discover that a table is clipping through a bookshelf.
And at this point the library wasn't just a few props.
I was placing:
- walls and architectural pieces
- real and decorative bookshelves
- tables and chairs
- rugs
- plants
- pavilions
- signs
- room entrances
- shelf rows
- collision boundaries
- lights
- eventually, empty spaces reserved for shelves that don't even exist yet
The first version of my workflow was basically:
change coordinates
↓
save
↓
reload the world
↓
walk to the room
↓
inspect the object
↓
realize it's wrong
↓
repeat
It worked.
It was also slowly driving me insane.
The obvious answer would have been to move the whole project into a traditional level editor.
But I didn't really want another application to become the source of truth for the library.
The architecture was already deeply connected to code:
DEV data
↓
district
↓
room
↓
shelf slot
↓
Three.js placement
Also, the Three.js editor was really freaking annoying to use.
At this point I realized that moving assets this way would either take forever or it would never be perfect to my liking. There had to be an easier way — and then I had an idea. Why don't we make the location of each shelf predetermined by a layout I create? Essentially a blueprint — then have Codex place the individual assets at each coord/pin that I place. So that became my next goal.
I needed to see coordinates inside the world
So instead I started building a tiny authoring system directly into Oniria.
The basic idea was simple:
If I'm standing somewhere in the world and think, "this object should go here," I should be able to record it here.
I added an in-world layout marker tool.
Now I could walk into a room, find the location visually, and drop a pin.
A marker could store something like:
{
"label": "R1-P01",
"roomSlot": 1,
"districtId": "latest",
"x": 16.154,
"y": 0,
"z": 5.369,
"yaw": 1.57
}
Suddenly I wasn't trying to mentally convert a 3D room into numbers.
I could use the room itself to produce the numbers.
The workflow became:
walk through the space
↓
find the location visually
↓
drop a marker
↓
capture position + rotation
↓
turn the marker into real architecture
That sounds like a small change.
It completely changed how I could build the library.
The world became my editor
For example, when I wanted to add an object to a particular room, I could physically stand where it belonged and create a marker.
Then that surveyed position could go directly back into the scene:
const surveyedPlacement = {
x: 16.154,
y: 0,
z: 5.369,
yaw: Math.PI / 2,
}
const instance = placeAsset(
someLibraryAsset,
surveyedPlacement.x,
surveyedPlacement.y,
surveyedPlacement.z,
1,
surveyedPlacement.yaw,
)
I started using the same workflow for things like:
"This shelf belongs here."
"This pavilion needs to move farther from the path."
"These two signs are colliding."
"This light needs to sit over this exact area."
"This decorative bookcase should sit between these DEV shelves."
Persisting the layout
I also didn't want those markers to disappear every time the browser refreshed. So the markers became Sanity documents.
A marker now has a persistent identity:
type LibraryLayoutMarker = {
id: string
label: string
roomSlot: number
districtId?: string
x: number
y: number
z: number
yaw?: number
width?: number
depth?: number
}
That means a layout session can survive reloads.
I can place several markers, come back later, and continue working from the same spatial notes.
I can also export them.
For example:
{
"format": "oniria-library-layout-pins-v1",
"markers": [
{
"label": "R1-P01",
"roomSlot": 1,
"x": 16.154,
"y": 0,
"z": 5.369
}
]
}
At this point I had accidentally built something halfway between a debugging tool, a survey tool, and a very small level editor.
And it solved a real problem immediately. Yay!
What started as a frustrating layout problem ended up teaching me something bigger about the project: if Oniria is going to treat the web as a place instead of a page, then the tools for building that place matter just as much as the content inside it. The DEV Library now has its own spatial logic, persistent layout markers, searchable shelves, dynamic slots, and an architecture that can evolve without losing its sense of place. I'm still building it room by room, but that's also the part I've started to enjoy most — every time the world gets difficult to work with, I end up building a new piece of the system that makes Oniria feel a little more real.









Top comments (8)
The interesting part here isn't really the 3D library itself. It's the moment where the problem stops being “how do I place this object?” and becomes “why am I still editing coordinates by hand?”
The marker approach makes a lot of sense because you're using the environment itself as the authoring interface. Instead of mentally translating a visual position into x/y/z values, you stand where the object should go and let the tool capture that state.
I also like the decision not to introduce a separate editor as another source of truth. Since the library already has its own data model from DEV data → district → room → shelf, keeping the layout information inside that system avoids creating another pipeline to keep synchronized.
The Sanity persistence is a nice touch too. Once the markers have IDs and can be exported, they stop being temporary debugging helpers and become actual layout data. It feels like a good example of a useful rule in tooling: when the workflow starts fighting the project, sometimes the right fix isn't changing the workflow. It's building the missing tool.
This is exactly the shift that happened while I was building it. At first I kept treating the coordinates as the problem, like i just needed to get better at estimating positions or organizing the values.
Eventually I realized the actual problem was that I was trying to author a spatial environment through numbers instead of through the environment itself. Once I could stand somewhere, drop a marker, and persist that state, the whole workflow started making much more sense.
Thanks for such a thoughtful read! 💚
...
Proceeds to build an entire game engine 😄
That's usually how it goes, though.
Your approach is really interesting. One big advantage I see is that the engine becomes reusable, so you can build other cool stuff on top of it.
And speaking of other cool stuff: I love the cyber city idea. It reminds me of Cyberpunk and Night City, which I'm a huge fan of, so I'd definitely love to see that too. Imagine reading DEV articles in City Center or the Badlands. Mmm... what an experience.
Okay, back to Earth: I can't wait to try out the library.
Great job, waiting to see it working 😃
Thank you! It's coming along nicely, designing the 3D layout has been my favorite part so far :)
i understand you 100% enjoy it!!!
Making the markers Sanity documents with stable IDs is the smart move here, it turns debug state into real layout data. Snapping yaw and position to a grid plus a bounding-box check on drop would catch most clipping before the walkthrough. Are the pins resolved from Sanity at runtime or baked in at build time?
Great job! Good luck!