Liquid Glass in C++
Screencast.From.2026-05-17.20-30-21.mp4
A high-performance experimental liquid glass rendering system built with OpenGL, GLFW, GLEW, and GLSL.
Because apparently rectangles were too emotionally stable.
Liquid Object creates draggable, morphing, refractive glass components with:
- Dynamic blur
- Real-time refraction
- Morphing liquid connections
- Polygon rendering
- Smooth spring physics
- Interactive highlights
- Neon blur tinting
- Apple-style liquid motion
- GPU-driven SDF rendering
The entire scene is rendered through a fullscreen fragment shader using signed distance fields (SDFs), which means the UI behaves more like fluid matter than traditional widgets.
- GPU accelerated liquid rendering
- Dynamic background blur
- Refraction simulation
- Edge specular lighting
- Interactive highlights
- Morph merging between nearby objects
Each object contains:
- Spring motion
- Velocity smoothing
- Elastic scaling
- Drag momentum
- Morph animation
- Hover interaction
The movement system intentionally behaves slightly organic.
Like expensive jelly.
| Shape Type | Description |
|---|---|
0 |
Rounded Rectangle |
1 |
Circle |
3 |
Polygon |
Polygon objects support:
- Triangles
- Pentagons
- Hexagons
- Octagons
- Custom vertex systems
sudo pacman -S glfw glew mesa glmsudo apt install libglfw3-dev libglew-dev mesa-common-devproject/
├── main.cpp
├── stb_image.h
├── bg.jpg
└── README.md
g++ main.cpp -o liquid_demo \
-lglfw \
-lGLEW \
-lGL \
-ldl./liquid_demoIf it instantly explodes with:
GLEW FAILED
then OpenGL context creation failed.
Usually because:
- GLFW window creation failed
- Missing OpenGL drivers
- Running through a VM with cursed graphics support
- Wayland/X11 issues
- NVIDIA deciding today is not your day
Human computing infrastructure remains a deeply unserious ecosystem.
| Key / Action | Result |
|---|---|
| Left Mouse Drag | Move objects |
N |
Spawn new liquid polygon |
| Move Objects Near Each Other | Morph connection effect |
The core system revolves around:
class LiquidObjectEach object represents a physically reactive glass surface.
LiquidObject obj(
"GlassCard",
200.0f,
200.0f,
160.0f,
160.0f,
0
);| Parameter | Type | Description |
|---|---|---|
name |
std::string |
Object identifier |
x |
float |
Initial X position |
y |
float |
Initial Y position |
w |
float |
Width |
h |
float |
Height |
type |
int |
Shape type |
LiquidObject card("Card", 100, 100, 180, 120, 0);LiquidObject orb("Orb", 300, 300, 120, 120, 1);LiquidObject poly("Hex", 500, 200, 140, 140, 3);
poly.numSides = 6;LiquidObject includes a chainable API.
Because typing the same object variable repeatedly is how civilizations collapse.
Enable or disable dragging.
obj.setDraggable(false);Enable liquid morphing behavior.
obj.setMorph(true);Change object shape dynamically.
obj.setShape(1);Changes blur tint coloration.
obj.setTint(0.4f, 0.1f, 0.8f);RGB values are normalized.
| Value | Meaning |
|---|---|
0.0f |
No intensity |
1.0f |
Full intensity |
Constrains movement along a line.
obj.setPath(
100.0f,
100.0f,
700.0f,
100.0f
);Useful for:
- Sliders
- Timelines
- Knobs
- Volume controls
- Dock systems
Changes corner radius.
obj.setRadius(32.0f);LiquidObject glass(
"PremiumGlass",
300,
240,
180,
180,
0
);
glass
.setTint(0.2f, 0.4f, 0.8f)
.setRadius(28.0f)
.setMorph(true)
.setDraggable(true);Every object contains:
LiquidParams params;| Field | Description |
|---|---|
blur |
Blur intensity |
tintAmount |
Glass tint strength |
refraction |
Distortion intensity |
chromatic |
Chromatic aberration amount |
cornerRadius |
Radius for rounded corners |
refractionHeight |
Refraction edge depth |
tintR/G/B |
RGB tint color |
obj.params.blur = 30.0f;
obj.params.refraction = 140.0f;
obj.params.chromatic = 0.06f;
obj.params.cornerRadius = 24.0f;Objects automatically merge visually when:
- Morphing is enabled
- Objects are close enough
- Distance threshold is reached
The shader uses:
smin()for smooth signed-distance blending.
This is the same category of technique used in metaballs and fluid rendering.
Which means you accidentally built procedural slime architecture.
Motion uses spring dampening.
Core equations:
velX = (velX + (targetX - offX) * 0.24f) * 0.74f;
velY = (velY + (targetY - offY) * 0.24f) * 0.74f;This creates:
- Inertia
- Elasticity
- Velocity carry
- Soft stopping
- Liquid feel
Objects dynamically stretch depending on velocity.
Fast horizontal motion:
- widens object
- compresses vertically
Fast vertical motion:
- stretches vertically
- compresses horizontally
This creates the “Apple liquid” feeling.
The same aesthetic principle:
Nothing should stop instantly.
Computers naturally want to move like bricks.
You are forcing them into pretending they understand viscosity.
Polygon rendering is fully procedural.
LiquidObject hex(
"Hexagon",
400,
400,
160,
160,
3
);
hex.numSides = 6;Press:
N
to create new liquid polygon objects at the mouse location.
Spawn limit:
32 objectsbecause the shader uniform arrays are fixed-size.
The GPU enjoys limits.
Unlike frontend developers.
The renderer is built around:
Functions include:
sdRoundRect()
sdCircle()
sdPolyEngine()The refraction system calculates:
- surface gradients
- edge normals
- refracted UV coordinates
Core effect:
refractedCoord = gl_FragCoord.xy + d * grad;Blur uses a lightweight Gaussian sampling pass.
gaussianBlurRaw()The blur radius dynamically changes during interaction.
Objects sharpen while actively morphing.
Which weirdly makes them feel more alive.
Humans are extremely susceptible to motion-based illusions.
Mouse proximity generates specular highlights.
lightSheenThis creates:
- reactive gloss
- directional lighting illusion
- premium UI feel
The shader supports embedded textures:
uniform sampler2D u_contentTex;You can inject:
- icons
- images
- text atlases
- UI layers
- procedural content
Current demo disables it:
glUniform1i(glGetUniformLocation(prog, "u_hasContent"), 0);Custom vertices are supported:
std::vector<std::pair<float, float>> customVertices;You can extend:
- stars
- spline shapes
- irregular blobs
- logos
- procedural meshes
The demo:
- renders entire scene in one shader pass
- uses fullscreen quad rendering
- performs SDF evaluation per fragment
Replace uniform arrays with Shader Storage Buffer Objects.
Move blur pipeline into compute shaders.
Hybridize CPU and GPU shape systems.
Reuse previous frame blur data.
Separate:
- blur
- lighting
- refraction
- composition
into dedicated render passes.
Because eventually every graphics project becomes:
“What if we invented an entire rendering engine by accident?”
Usually means:
- shader compile failure
- OpenGL version mismatch
- texture failed loading
Check:
if (glewInit() != GLEW_OK)Also ensure:
glfwMakeContextCurrent(window);was called first.
Ensure:
bg.jpg
exists beside the executable.
- Liquid sliders
- Glass buttons
- Fluid dock
- Dynamic blobs
- Audio reactive motion
- Soft-body physics
- Runtime shaders
- Particle systems
- Liquid tabs
- Real UI toolkit
At some point this stops being a demo and becomes:
“someone accidentally rebuilt Apple Human Interface Guidelines using pure GLSL and insomnia.”
Licensed under the Apache License 2.0.
See:
LICENSE
for details.
Built using:
- OpenGL
- GLFW
- GLEW
- GLSL
- stb_image
And a completely unreasonable amount of shader math.
Liquid Object is not a traditional UI framework.
It is closer to:
- procedural graphics
- liquid simulation illusion
- shader-driven interaction design
- experimental interface rendering
The important idea is:
The UI should feel physically alive.
Most interfaces still move like PowerPoint slides from 2009.
This one at least tries to behave like matter.
cmake --build cmake-build-debug --target liquid_demo -j 6