Real depth for Ocean's elevation: a design question for Union

Hi @ahiemstra and @akselmo, and Andy Betts if he reads here.

I have a design question for Union, and it comes from an unusual place, so a little context first.

Ocean describes elevation the way every flat desktop does: a ladder of shadows, drawn in 2D. We built a Plasma desktop where elevation is real. Every element sits on one of three planes, sunk, screen or popped, and the depth is drawn for real:

  • On a normal 2D screen, a popped element gets a little larger and moves slightly up and left, like an object coming closer. Its text and its click area follow it, so nothing becomes a dead zone.
  • On a stereo screen (a 3D TV, an anaglyph monitor with paper glasses, a headset), the same element gets the same small scale plus the two eyes’ disparity, so it is physically in front of the screen.

One value per element, the same on every screen, and the user sets how far “popped” goes. Background windows flatten into a pile of sheets, the way papers look on a desk.
The rules are short: GitHub - Sparky-OS/sparky-stereo-os: Sparky Stereo OS 9 "Mashtabba", based on Debian 14 "Forky": the stereo 3D edition of SparkyLinux, KDE Plasma only. · GitHub

I know “3D on the desktop” sounds like the 2010 television fad. This is not that. Nothing is forced and nothing jumps out at you; it is elevation, done for real. The compositor side is a one-page design: everything hands KWin one stereo format, and each screen gets the output it needs, including plain 2D.

The question: where would a depth value belong in Union’s CSS? I picture it next to the shadow: three values (sunk, screen, popped) plus inherit, which Ocean’s elevation ladder maps onto. Union would declare the plane; the compositor draws it for whatever screen is attached. Is that the right place, or does it belong somewhere else in Union’s model?

Who a stereo desktop is for: it goes far beyond games
  • Science and research: molecules, anatomy, geology and seismic data, microscopy, through the stereo modes FreeCAD, ParaView and VTK-based tools already have, which today need a workstation card to use.
  • Engineering and mapping: circuit boards in KiCad, terrain in GRASS GIS, meshes in Netgen, models in OpenSCAD, plots in Octave and Matplotlib; all of these already run in stereo on our stack.
  • Education: KAlgebra’s 3D graphs, Kalzium’s molecules, and teaching material that needs depth to be understood.
  • Digital twins: cities, plants and islands represented in depth.
  • Medicine and training: surgery practice, anatomy, industrial training and design review.
  • Heritage and tourism: museums, archaeology and places people cannot travel to; accessibility for those whose bodies cannot take them there.
  • Photography and art: stereo photos (MPO, JPS), painting a stereo pair in Krita, editing in Kdenlive with a 3D preview.
  • Media: 3D films and documentaries, broadcast archives, recording and streaming in stereo, and remote desktops that keep their depth.
  • Installations with ageing stereo hardware: science centres, theme parks and simulators whose playback machines need a maintained system.
  • And games and VR.

Why it matters for Plasma itself: Valve ships Plasma as SteamOS’s desktop, and its hardware now includes a headset. A Plasma whose elevation is real depth is a Plasma that works in a headset without a separate interface.
Our Desktop Cube design shows where that leads: GitHub - Sparky-OS/sparky-stereo-os: Sparky Stereo OS 9 "Mashtabba", based on Debian 14 "Forky": the stereo 3D edition of SparkyLinux, KDE Plasma only. · GitHub

What exists today:

All of this ships first in Sparky Stereo OS, an upcoming edition of SparkyLinux. For Union and Ocean, I would rather ask before building anything.

In the open: I direct this work and verify every result myself, with AI as an assistant; the evidence is in the links.

Daniel

1 Like

Thanks for bringing this up. The Ocean design system already has different shadow values to indicate different depth levels.

1 Like

Thanks, @Anditosan.
Yes, and Ocean’s shadow levels are exactly what I want to build on, not replace.

A shadow is how a flat screen draws depth.
On a stereo (real 3D, xyz) screen a shadow alone contradicts itself: it says the popup is raised, while both eyes see it flat on the glass.

So a stereo screen needs the level itself, not only its drawing.

Today Union carries only the drawing.
A popup gets box-shadow: 0px 2px 20px 0px rgba(0, 0, 0, 0.2), a dialog gets 0px 1px 32px 0px rgba(0, 0, 0, 0.3), and by the time the style reaches the renderer the level has become offset, blur and colour.
Nothing downstream can tell which step of the ladder an element is on.

The proposal is small.
The element declares its plane (sunk, screen or popped), and Ocean’s shadow values become the shadow strength of each plane.
On a 2D screen nothing changes: same shadows, same tokens.
On a stereo screen the compositor adds the disparity for that plane, and the shadow finally agrees with what the eyes see.

So my question is where that one value should live in Union: next to shadow in the style properties, or somewhere you would prefer.

I don’t know this myself, but we can ping @ahiemstra or @akselmo

1 Like

It seems like you’re talking about two related but different things:

  1. Stereoscopic window decorations (e.g., window shadows). This doesn’t have much to do with Union since the Wayland compositor should handle that. You’d probably want to rely on server side decorations (SSDs) to have real 3D shadows. It is likely that apps with client side decorations (CSDs) will not be able to use server side decoration shadows.
  2. Stereoscopic UI elements within a window. UI element theming within a window is handled by Union, but I don’t see how any of us could make this work without rewriting all of our apps to use a 3D graphics toolkit like Qt Quick 3D. With the right math, you can still make 2D graphics look a lot like real 3D graphics, but it’s a lot of effort and I don’t see us giving any priority to that while we’re still working out bigger details.
1 Like

Thanks, @ndavis, this helps me be clearer, because no app rewrite and no 3D toolkit are involved.

Depth on a stereo screen is not 3D geometry.

It is a horizontal shift: each eye sees an element a few pixels left or right of where the other eye sees it, and that is all a 3D TV or a headset needs to place it nearer or further.

The elements stay flat, the way a sheet of paper on a desk is flat.

So the work splits into three places, and only one of them is Union’s:

  • Windows, their shadows, and everything that is already its own surface (menus, tooltips, popups, dialogs, notifications): the compositor shifts each surface for each eye. That is KWin’s job, and our KWin branch already does it: the active window at the screen, its menus and tooltips popped, the windows behind sunk, and each shadow lying on whatever it falls on.
  • Elements inside a window (buttons, cards and their shadows): Union already draws every one of them through its own renderer (the StyledRectangle nodes in Qt Quick). For a stereo screen the same nodes are drawn once more, shifted by the element’s parallax. That is one place in Union’s renderer; the apps do not change.
  • The value: which plane an element is on, sunk, screen or popped. That is the question I asked, and the only thing I am asking Union for: one property, next to the shadow, that both the renderer and the compositor can read.

On a 2D screen nothing changes.

It is not a priority request either: I am asking where the value would belong, so that what we build follows Union’s model instead of working around it.

PS:

We are not asking anyone to code this.

The KWin side is written and its tests pass, and we will write the Union side too, in Union’s own style.

It will reach you as finished, tested patches; all we need from you now is where the value belongs, so that what we send fits your model and only needs your review.

A short update: the compositor’s side can now be built and seen without a 3D display.

The KWin part of the three planes (sunk, screen, popped) is public and green on KDE’s CI, in the stereo3d-depth branch of my KWin fork on invent (danielcamposramos/kwin).
It covers what @ndavis described as the compositor’s job: windows and their shadows at a depth, a shadow lying on what it falls on, the pointer taking the level of what it is over, and a settings page for the depth.
To see it on an ordinary monitor, KWin can make any screen a virtual anaglyph monitor, so red and cyan paper glasses are enough.
The design in one page is the “Depth on the desktop” section of the Sparky Stereo OS project page on GitHub.

The question for Union stays the one from my first post: where should an element’s depth value live, so the Union side we write fits your model?

We will write it in Union’s style and send it as tested patches.

Treating elevation as geometric depth rather than purely 2D shadow gradients works well conceptually, but Union’s layout and rendering pipeline requires a strict separation between logical elevation and physical display projection. If popped elements scale up and translate diagonally, input coordinate mapping and clipping boundaries must be handled at the compositor level rather than inside the application style to prevent hit-test drift or layout reflow. A fixed three-plane model (sunk, surface, popped) keeps contrast and legibility predictable across standard 2D displays while letting stereo disparity map cleanly from the same elevation token without altering standard Ocean padding or typography hierarchy.

1 Like

Thanks, @chineduokafor, that is how we built it.
An element’s plane is applied when it is drawn, never when it is laid out, so Ocean’s padding and typography stay exactly as they are.
On a stereo screen the disparity is drawing only: each eye’s copy is shifted a few pixels, and input keeps the element’s ordinary 2D position, so hit-testing cannot drift.
That is what our KWin branch does for windows today: the shift is applied per eye when the scene is painted, and hit-testing, the pointer’s level included, uses the window’s 2D geometry.
Inside a window, the small scale and shift a popped element gets on a 2D screen would be an item transform, and Qt Quick maps input through item transforms, so the click area follows without any reflow.
So the open question stays the small one, for @ahiemstra and @akselmo: where should an element’s plane live in Union, so both the renderer and the compositor can read it?

I dont understand the question. Union only dictates style of things, not where they live in the hierarchy.

1 Like

Thanks, @akselmo, for the time to answer.
I mean a style property, the same kind as box-shadow, not a place in the item hierarchy.

Concretely: would Union accept one more property in properties.yml, for example depth: sunk | screen | popped | inherit, which the style sheets set the way they set box-shadow today (menus and tooltips popped, window content at screen, and so on)?
Union’s renderer and KWin would read it to draw the element at its depth; the layout never changes.

If you would rather not add a property, the other way we see is to read the level from the shadows Union’s Breeze style already sets (blur 1 for a button, 18 for a card, 20 for a menu or tooltip, 32 for a dialog).

Which of the two fits Union better?