Doop
Doop: 오픈소스 멀티플레이어 디자인 캔버스 및 AI 에이전트용 MCP
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기Stretchy Liquid Glass (iOS style) for MacOS and Websites
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Stretchy Liquid Glass (iOS style) for macOS and websites.
AppKit buttons for macOS 26 that swell when pressed, follow your drag, stretch along the pull,
and wobble back — plus a dependency-free web version you can try in a browser.
If this made you smile, a ⭐ helps others find it.
macOS 26's NSGlassEffectView gives you the Liquid Glass material. It does not give you the feel — the
way glass in a polished app gives under your finger. JellyGlass adds that feel, and writes down what it took
to get it working inside AppKit's glass containers (it is less obvious than it sounds — see
How it works).
The numbers are not guessed: they were measured, frame by frame, from a screen recording of a production app (MEASUREMENTS.md). So was the lens of the system glass that the web version copies (HOW_IT_WORKS.md §10).
In the browser (any modern browser, no install): https://54degree5.github.io/JellyGlass/
Download the demo app (macOS 26+, Apple silicon and Intel): latest release.
It is ad-hoc signed, not notarized: unzip, then right-click → Open (or xattr -dr com.apple.quarantine JellyGlassDemo.app).
Or build it (macOS 26+, Xcode 26 / Swift 6.2+):
git clone https://github.com/54degree5/JellyGlass.git
cd JellyGlass
swift run -c release JellyGlassDemo
Drag any button. Keys: K on top ⇄ merge · S spring (jelly / wobble / snappy) · A stretch
(standard / wide / heavy) · C glass style.
// Package.swift
.package(url: "https://github.com/54degree5/JellyGlass.git", branch: "main")
import JellyGlass
let mic = JellyGlassButton(size: CGSize(width: 44, height: 44), symbols: ["mic"])
mic.spring = .jelly // .jelly (default) · .wobble · .snappy
mic.stretch = .standard // .standard · .wide · .heavy
mic.liftsOnPress = true // false: melt into neighbours instead
mic.look = .transparent // default: clear refractive glass, the scene behind stays visible; .system = AppKit's frosted material
// Optional: group buttons so neighbours share one glass surface and merge when `liftsOnPress` is false.
let group = NSGlassEffectContainerView()
group.spacing = 14
let holder = NSView(frame: group.bounds)
holder.addSubview(mic)
group.contentView = holder
Any view works as content: JellyGlassButton(size:contentView:). The button deforms its glass and its content.
With d the cursor's distance in points from where the button was pressed:
| formula | at d = 38 / 80 / 185 / 800 pt | |
|---|---|---|
body travel t(d) | 6.5 · ln(1 + d / 28) | 5.6 / 8.8 / 13.2 / 22 pt |
stretch e(d) | 0.105 · ln(1 + d / 50) · d² / (d² + 40²) | 0.03 / 0.08 / 0.16 / 0.30 |
Beyond ~200 pt the recorded body travel plateaus (it contains a single long flick), but by hand the app keeps stretching, so the model deliberately keeps growing — slowly, without a ceiling (see the shaded area below).
Any size. The numbers above come from a 44 pt circle. For other sizes the absolute change follows them, not the
ratio: along the pull the factor is 1 + e·√(44/L), across it 1 − (1 − 1/(1+e))·(44/L), where L is the button's length
on that axis. A 44 pt circle behaves exactly as measured; a 108 pt pill pulled down keeps its width (−2 pt instead of
−10 pt); a 360 pt bar pulled sideways grows by ~9 % instead of ~30 %. Press swell scales the same way. (The AppKit glass
margin grows with the width too, so long buttons are never clipped.)
Pulled along an axis the shape scales ×(1+e) along it and ÷(1+e) across it. Pulled diagonally the
axial part fades out and the whole button grows 1 + 0.6·e instead. Springs (stiffness / damping):
| preset | held | after release |
|---|---|---|
jelly | 1500 / 70 | 420 / 12 — swings past rest once or twice |
wobble | 1500 / 70 | 240 / 6 |
snappy | 3600 / 120 | 3600 / 120 — back in ~60 ms |
The short version of what is not in any documentation:
NSGlassEffectView is made larger than the button (a 90 pt margin) and its silhouette is set with
the private _setPath: — the resting capsule, scaled and shifted every frame — and the container is asked
to re-layout each frame so it re-reads the path. The icons live in the content view with their own spring._interactionState = 1 lifts a glass out of its group and draws it on top of its neighbours
(a highlight, no merging). That is the default "on top" behaviour; leave it off and the button melts
into neighbours instead.spacing / 2, but they
start leaning toward each other already below spacing. Resting gap ≥ spacing avoids pointed tips at rest.Full write-up with the layer tree and the dead ends: docs/HOW_IT_WORKS.md.
docs/index.html is a single file, no dependencies. Open it and, after a moment, a cursor starts
pressing the buttons and wandering with them (press anything to take over; ▶ demo brings it back).
The glass is real, not a CSS imitation: a WebGL2 fragment shader draws the scene behind the buttons and draws it again through each button's capsule. Near the rim the sample point is moved along the edge normal by a profile measured from the real macOS 26 glass (the outer rim shows the inside of the glass flipped, then comes a short magnified band, then the glass is 1:1), with a hair of dispersion, a light blur and a thin bright contour that picks up the colour under it (how it was measured). Each capsule follows its button's jelly motion every frame, so the refraction stretches and wobbles with it (120 fps on a retina MacBook). The physics is the same as the AppKit version: same formulas, same springs.
Any shape, any length: docs/shapes.html runs the same physics on a circle, a
pill, a 560 px bar, a squircle, a star, a heart, a hexagon, a triangle, a plus sign and a wobbly blob. A shape is just a
signed-distance function in the shader; refraction, rim and shadow come from its gradient, so any closed shape works.
In AppKit the private _setPath: takes any CGPath.
Two honest limits: the glass refracts its own scene (a shader cannot see the DOM, so it works because the background
is drawn by the same shader), and without WebGL2 the page falls back to a frosted CSS glass. Making it refract arbitrary
page content needs a page capture or Chromium's SVG backdrop-filter; contributions welcome.
_setPath: and _interactionState are private AppKit selectors, resolved at runtime
(they exist on macOS 26), and the clear look edits the inputs of the private glassBackground backdrop filter. Fine for tools and experiments, not for the App Store. If a selector goes missing
the call is skipped and the button degrades to icon animation only.NSGlassEffectView.JellyGlass { … })Issues and PRs welcome — especially measurements from other apps (record the screen, drop the file in an issue), and anything that makes the AppKit side less private-API-dependent.
MIT — see LICENSE.