- Apple이 2025년 WWDC에서 공개한 Liquid Glass 효과를 웹에서 CSS와 SVG, 물리 기반 굴절 계산으로 재현하는 방법 소개임
- 굴절 현상의 원리와 Snell의 법칙을 활용한 유리 표면 묘사와 굴절 시뮬레이션 과정을 설명함
- SVG displacement map 이용해 렌더링에 적합한 변위 벡터 필드를 생성하고, 실제 UI 컴포넌트에 적용 가능함을 시연함
- Chrome에 한해 SVG filter를 backdrop-filter로 활용 가능하며, 다양한 UI 요소(돋보기, 스위치, 플레이어 등) 에 적용하는 예시를 제시함
- 실시간 효과 구현이 가능하지만 크로스 브라우저 문제 및 퍼포먼스 제한이 있고, 향후 오픈소스 계획이 있음을 언급함
소개
Apple은 2025년 6월 WWDC에서 Liquid Glass 효과를 최초로 선보였음. 이 효과는 인터페이스 요소가 곡선의 굴절 유리처럼 보이도록 해 실제 유리 표면과 유사한 시각적 경험을 제공함. 이 글에서는 웹 환경에서 CSS, SVG displacement map, 물리 기반 굴절 계산을 통해 유사한 효과를 만드는 실습을 다룸. 완전한 구현은 목표가 아니고, 굴절과 반사 하이라이트 등 주요 특징을 재현한 확장 가능한 개념 증명을 목표로 함. 빛이 서로 다른 재질을 통과할 때 어떻게 굴절되는지 기초 원리로부터 시작해 단계별로 효과를 구축함. 마지막에 제공되는 인터랙티브 데모는 현재 Chrome에서만 정상 동작함.
굴절 현상의 이해
굴절이란 빛이 한 재질에서 다른 재질로 이동할 때 진행 방향이 바뀌는 현상임. 각 재질마다 빛의 속도가 다르기 때문에 발생하며, Snell의 법칙으로 입사각과 굴절각의 관계가 수식으로 표현됨:
- n1 * sin(θ1) = n2 * sin(θ2)
- n1: 첫 번째 매질의 굴절률
- θ1: 입사각
- n2: 두 번째 매질의 굴절률
- θ2: 굴절각
인터랙티브 다이어그램에서 확인할 수 있는 것:
- 두 매질의 굴절률이 같으면 빛은 굴절 없이 직진함
- 두 번째 매질이 더 높은 굴절률일 때 빛은 법선 방향으로 굴절함
- 두 번째 매질이 더 낮은 굴절률이면 빛이 멀어지며, 경우에 따라 내부 전반사가 발생할 수 있음
- 입사광이 표면에 수직이면 굴절률과 무관하게 직진함
이 프로젝트의 한계
복잡도를 제한하고 알고리듬을 단순화하기 위해 다음과 같은 조건을 설정함:
- 외부 매질 굴절률은 1 (공기)
- 내부 유리 재질은 1.5 사용 (일반적인 유리)
- 굴절 이벤트는 한 번만 고려 (출구에서의 굴절 무시)
- 입사광은 항상 배경면에 수직
- 모든 오브젝트는 2D 도형, 원 형태만 사용 (직사각형 등 확장 가능하지만 계산 필요)
- 오브젝트와 배경면 사이에 틈이 없음
- 이 조건을 통하면 Snell의 법칙으로 모든 광선을 단순 계산 가능
유리 표면 만들기
Liquid Glass 효과 구현을 위해선 가상 유리의 단면(렌즈 또는 곡면 판넬)을 수학적 함수로 정의해야 함.
표면 함수
유리 표면은 surface function으로 정의되며, 가장자리에서 평평한 내부까지의 두께를 나타냄
- 함수 입력값 0: 외곽, 1: 베젤 끝 (평면 시작)
- 각 점의 두께에서 도함수를 통해 표면의 법선 벡터 구함
const height = f(distanceFromSide);
const delta = 0.001;
const y1 = f(distanceFromSide - delta);
const y2 = f(distanceFromSide + delta);
const derivative = (y2 - y1) / (2 * delta);
const normal = { x: -derivative, y: 1 };
주요 표면 함수 유형
-
Convex Circle: y = sqrt((1 - (1 - x))^2)
- 간단한 원호 형태, 내부로 갈수록 급격하게 평평해져 굴절 가장자리가 뚜렷함
-
Convex Squircle: y = ((1 - (1 - x))^4)^(1/4)
- Apple에서 즐겨 사용하는 형태, 곡선과 평면의 경계가 부드러워 확장 시에도 효과가 자연스러움
-
Concave: y = 1 - Convex(x)
- 오목(볼록 반대) 형태, 빛이 오브젝트 경계 바깥으로 굴절되어 바깥 샘플링이 필요함
-
Lip: y = mix(Convex(x), Concave(x), Smootherstep(x))
- 가장자리에 돌출된 립, 중앙에 얕은 굴곡이 있는 복합 구조
이 네 가지 함수만으로도 표면 형상에 따른 굴절 차이를 효과적으로 비교 가능함
시뮬레이션
각 표면 함수에 따라 광선의 굴절 경로를 시뮬레이션하여 실제 효과 차이를 시각화함.
- 볼록(Convex)은 빛의 경로를 내부로 묶고, 오목(Concave)은 외부로 밀어냄
- Apple의 Liquid Glass는 대부분 볼록 형태를 선호 (Switch 등 예외)
- 배경 화살표는 굴절량(변위)을 마그니튜드로 색을 입혀 표시함
- 좌우 경계에서 같은 거리에선 동일한 변위를 가져 효율적 재사용 가능
변위 벡터 필드 생성
유리 표면 전체에서 각 위치별로 빛의 변위 방향과 크기를 나타내는 벡터 필드를 구축함
- 원형에선 경계 기준으로 항상 법선 방향으로 이동
변위 크기 미리 계산하기
- 변위 크기는 경계로부터의 거리마다 대칭이므로 반지름 단위로 미리 값들을 계산해 배열로 저장
- 2D에서 한 번만(127개 레이 시뮬레이션) 계산 후, z축을 중심으로 회전해 전체 필드 생성
벡터 정규화
벡터를 displacement map에 쓰기 위해 정규화(최대 1.0 스케일) 수행
- 최대 변위값을 기준으로 나머지 벡터 크기를 나눠 정규화
const maximumDisplacement = Math.max(...displacementMagnitudes);
displacementVector_normalized = {
angle: normalAtBorder,
magnitude: magnitude / maximumDisplacement,
};
SVG displacement map에서 실제 픽셀 단위 변환 시 scale로 다시 최대 변위값을 곱해 원래 크기 복구
SVG Displacement Map
수학적 굴절 계산 결과를 실질적으로 브라우저 렌더링에 적용하기 위해 SVG displacement map을 사용함
-
<feDisplacementMap />
의 각 채널(RGBA, 8비트)은 각각 변위의 X, Y축을 담당할 수 있음 - 각 채널이 0~255의 값을 갖고, 128이 중성(변위 없음)을 의미함
- displacement map은 반드시 이미지로 변환 필요
<svg colorInterpolationFilters="sRGB">
<filter id={id}>
<feImage
href={displacementMapDataUrl}
x={0}
y={0}
width={width}
height={height}
result="displacement_map"
/>
<feDisplacementMap
in="SourceGraphic"
in2="displacement_map"
scale={scale}
xChannelSelector="R"
yChannelSelector="G"
/>
</filter>
</svg>
Scale
Red(X)와 Green(Y) 채널의 값을 [−1, 1] 범위로 맵핑
- scale 속성으로 픽셀 단위 변위 최대치 곱해 실제 렌더링 구현
- scale을 애니메이션해 효과 강도 조정 가능
벡터 → Red/Green 값 변환
- 변위 벡터(각도, 크기)를 x, y 직교좌표로 변환 후, 128(중성) 기준으로 0~255로 맵핑
const x = Math.cos(angle) * magnitude;
const y = Math.sin(angle) * magnitude;
const result = {
r: 128 + x * 127,
g: 128 + y * 127,
b: 128,
a: 255,
};
완성된 이미지는 SVG filter displacement map으로 활용 가능
Playground
인터렉티브 Playground에서는 표면 형태, 베젤 두께, 유리 두께, effect scale 등을 실시간 변경하며 굴절 필드, displacement map, 최종 렌더링 변화를 체험할 수 있음
Specular Highlight
마지막으로 specular highlight(유리 표면의 밝은 엣지 하이라이트)를 추가함
- Apple의 구현은 rim light(가장자리 라이트) 방식으로, 표면 법선과 광원 각도에 따라 밝기 강도가 달라짐
굴절과 Specular Highlight 결합
최종 SVG filter에선 displacement map과 specular highlight 이미지를 각각 <feImage />
로 불러오고 <feBlend />
로 합쳐 최종 효과를 생성함
- filter 파라미터를 조정해 다양한 비주얼을 연출할 수 있음
SVG filter를 backdrop-filter
로 사용
- 실질적으로 UI 컴포넌트에 Liquid Glass 효과를 입히려면, Chrome의
backdrop-filter: url(#...)
지원이 필요함 -
backdrop-filter
이미지 크기가 자동으로 맞춰지지 않으니, element 크기에 맞는 displacement map 준비 필수
.glass-panel {
backdrop-filter: url(#liquidGlassFilterId);
}
실제 UI 컴포넌트 적용
계산한 refraction과 displacement map을 바탕으로 현실적인 UI 컴포넌트에 적용 예시 구현
- Chrome만 SVG filter를
backdrop-filter
로 처리할 수 있음 - 예시들은 진짜 프로덕션 컴포넌트가 아니라, 다양한 UI에 Liquid Glass 효과가 적용되는 모습을 시연하는 목적
Magnifying Glass
- 양쪽에 refraction 및 zoom, 두 개의 displacement map을 사용
- 그림자, 스케일 조정으로 인터렉티브한 효과 부여
- 드래그로 렌즈 변형, 굴절 경로를 관찰 가능
- 부드러운 specular highlight 추가
Searchbox
- 표준 입력창 형태에 Liquid Glass 효과 적용
Switch
- lip bezel 사용해 외부는 볼록, 내부는 오목
- 중앙 슬라이더만 확대/축소된 상태, 가장자리는 내부 이미지 굴절
Slider
- convex bezel을 사용, 현재값을 유리를 통해 그대로 보여주고, 양쪽은 배경 굴절 적용
Music Player
-
Apple Music의 Liquid Glass panel 스타일을 모방
-
convex bezel, subtle specular highlight로 입체감 부여
-
iTunes Search API를 활용해 앨범 아트와 곡명 등 정보를 로드
-
(리스트 형태의 곡명 및 앨범 정보 제공)
결론
이 프로토타입은 Apple의 Liquid Glass 개념을 실시간 굴절 효과와 간단한 하이라이트로 단순화해 표현함. Chromium 기반 브라우저(또는 Electron)에서만 실질적으로 동작 가능하며, 타 브라우저에서는 blur 레이어로 대체가 가능함.
이는 실험적인 구현이며, shape/size 변화마다 displacement map 재생성이 매우 비효율적임(필터 scale
등 일부 파라미터 만 애니메이션 가능).
추후 오픈소스 공개를 검토 중이며, 최적화 및 코드 정리에 관심이 있음을 밝힘.
Hacker News 의견
- WebGL 셰이더로 비슷한 기능을 만든 경험이 있음, 여러 브라우저에서 동작하는 장점이 있음, https://real-glass.vercel.app 공유함, 어려웠던 점은 실제 HTML 요소 뒤에서 굴절 효과를 제대로 재현하는 부분이었음
- 유리 효과를 텍스트 위에 올려서 움직일 때 생기는 고스트 현상/딜레이는 무엇이 원인인지 궁금함
- 너무 멋지다고 생각함, 가장자리에서 색이 분리되는 분산 효과까지 구현된 것처럼 보임
- 비주얼은 멋진데, 실제로 사용하기엔 반응 속도가 너무 느림, 원 게시자의 것이 훨씬 부드럽게 동작함
- 인상적임
- 스크롤을 내릴 때마다 M4-Max Macbook Pro에서도 버벅임이 생기는 점이 인상적임, 만약 전체 UI에 이런 기술이 적용된다면 성능면에서 걱정이 앞섬, Apple에서 가능한 이유는 극도로 최적화했기 때문일 것임
- 본문 작성자로서 성능 이슈는 미리 고치려 했으나 누군가가 예상보다 먼저 HN에 게시해버림, 지적한 내용이 맞아서 지금은 다소 느리고 추가적인 최적화가 필요함, 굴절/변위 맵 뿐만 아니라 시각화 등 다른 부분도 아직 최적화가 안 되어 있음
- 크롬에서 성능 개선을 빠르게 진행했기 때문에 약간 더 좋아졌을 것임, 사파리에서는 SVG 렌더링이 여전히 느림, 예상치 못하게 게시되어 더 개선할 부분이 남아 있음
- 이 사이트는 스크롤이 부드럽게 되지 않음, 대부분의 경우 CSS는 GPU를 잘 활용하지 못함, Apple은 UI 처리를 위해 실리콘에 특수한 처리를 추가한 것으로 보임
- 내 컴퓨터에서도 같은 현상, 테두리 효과도 제대로 보이지 않았음
- 내용도 좋지만, 전체 기사 구성과 퀄리티 또한 훌륭했음, Liquid glass라는 개념 자체가 실질적인 UX에는 특별히 추가적인 이점을 주진 않음(오히려 과하게 쓰면 UX를 해칠 수도 있음), 하지만 신선하고 즐거운 경험임
- chrome 전용 데모임을 강조하며, 마지막 인터랙티브 데모는 Chrome에서만 동작함(SVG filter로 인한 backdrop-filter 제약 때문), 타 브라우저에서도 본문 읽기나 간단한 시뮬레이션은 가능함이라고 작성함. 이에 대해 "네 가족 모두에게 불명예를!"이라는 유머러스한 반응이 있음
- 이런 방식이 허용되는 건 불가피함, 해당 기능은 특정 브라우저에서만 지원되는 것을 보여주기 위한 목적임
- 재미있게도, 내 경우에는 Chrome에서 페이지가 더 느리고 스크롤도 더 버벅거리는 반면, Firefox에서는 지원되지 않는 효과임에도 불구하고 오히려 더 부드러웠음, 그래도 기사 자체에는 매우 감탄했음
- 나 역시 비슷한 반응이었지만, 특이하게도 FireFox에서도 꽤 괜찮게 보였음
- 레퍼런스를 궁금해 한다면, https://youtu.be/GamP4chXJ2I?t=17 을 참고할 수 있음
- liquid glass 디자인 언어가 웹에 적용될 것임은 예상된 일이었음, 하지만 텍스트 왜곡 때문에 웹사이트가 내 배터리를 축낼 경우에는 오래 머무르지 않을 예정임, 이미 많은 이가 버벅임을 지적했기에 추가 언급은 하지 않겠음
- 멋지고 정성이 느껴지는 작업임, 하지만 liquid glass라는 것은 서로 가까운 요소가 메타볼처럼 합쳐지거나, 다양한 틴팅/클리어 모드, 컨트롤이 콘텐츠와 분리된 레이어에 있는 등 디자인 언어 전체를 의미함, 이번 구현은 말하자면 단순 '글래스 셰이더'에 가까움
- 요소 합치기는 훨씬 단순한 필터인 'Goo' 필터로 해결할 수 있음, 예전부터 사용하던 방식임, 참고용 구현: https://codepen.io/lenymo/pen/pJzWVy
- liquid-glass용 JS 라이브러리를 포크해서 위치 수정 패치를 추가함, 프레젠테이션할 때 사용하면 재밌음, 소스코드: https://github.com/nkzw-tech/liquid-glass
- 멋짐, 오히려 이쪽이 더 마음에 듦
- Firefox에서 일부 효과만 동작하지만(대신 성능 확보!), 지금까지 본 중에서 최고의 구현임, 최근 며칠간 관련 리서치를 많이 했기에 더 인상적임, 가장 마음에 들었던 건 웹사이트의 디자인과 정성 들인 인터랙티브 시각화였음, Bartosz Ciechanowski와 Josh Comeau의 작품과 동급이라고 느낌, 소스코드가 공개되길 바람
- 브라우저 지원 제약에도 불구하고 훌륭한 시도라고 생각함, 인라인 인터랙티브 예시가 추가적 가치를 더했음, 어느 순간엔가 Ciechanowski의 글을 읽는 느낌이었음(참고: https://ciechanow.ski/)
- 새로 나온 ray-traced 스크롤바와 버튼이 실제로 더 기능적이며, 예전의 텍스트 모드 터보 비전이나 Windows 3의 버튼보다 생산성이 향상되는지 궁금함