VS Cylinder Label Wrap
Getting flat label art to sit on a curved bottle
- label
- coverage
- reference
- label
- mask
Paste a flat label onto a photo of a bottle and it reads as fake instantly - the type doesn't compress toward the edges, the artwork doesn't disappear around the curve, and there's no falloff into the silhouette. VS Cylinder Label Wrap treats the bottle body as an orthographic cylinder and projects your flat label onto it properly, with a mask that fades out at the horizon. It's the single most calibration-heavy node in the pack, and the only one where I'd tell you to budget an hour of nudging before your first good result.
The mechanism
This one's worth understanding rather than blindly setting, because when it's wrong the fix isn't obvious. For output pixel x, the node computes the normalised offset from the bottle's centre line, s = (X - center_x·W) / (radius·W), treats that as sin(θ), and derives the label's horizontal coordinate from asin(s) - so label space compresses toward the edges the way real printed art does as it curves away. Vertically it's a plain linear map between top and bottom. Then a single grid_sample pulls the label through that mapping.
Two engineering details that make it look expensive (it isn't, it's just well-built): the label is pre-shrunk to roughly one label pixel per output pixel at the front of the bottle before the warp with antialiasing, so small type doesn't alias; and supersample (default 2, max 3) renders the whole warp at 2× or 3× and downsamples after.
The mask is where the realism lives. It's coverage × a one-pixel vertical antialias at the label's top and bottom edges × a smoothstep horizontal hand-over that starts at edge_fade_start of the radius and completes at edge_fade_end. The author's tooltip: "Fraction of the radius where the label starts handing over to the base image edge." Past the horizon the mask is zero, so the label vanishes into the silhouette instead of getting clipped to a hard line.
The calibration
center_x, radius, top, bottom, u_center and u_scale are all fractions - x and y as fractions of canvas width and height, on the pack's reference 9:16 canvas. The defaults (center_x 0.499, radius 0.213, top 0.514, bottom 0.853, u_center 0.280) describe a 12oz crown-cap bottle padded to 9:16 at 1487×2644. The README is honest that these hold for "a different bottle" only after you refit them. If your bottle is 330ml, or padded to 4:5, or off-centre, every number here is wrong and it will look wrong in a way that's hard to read - label half off the bottle, or wrapped onto the neck.
u_scale lets you stretch the flat art's width, which is the cheat for when your label artwork has different margins than the die line. reference is the image whose canvas defines the frame - the base plate you're wrapping onto - while label is the flat art and coverage is the mask from VS Label Photo Slot.
Outputs are label and mask: a full-canvas image with the wrapped art painted in, plus the mask saying where it counts. That pair goes straight into VS Label Shade + Composite.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/andrewclass1/comfyui-vs-glassmatte
# restart ComfyUI
Or ComfyUI Manager → search VS Glass Matte. No requirements.txt, no dependencies in pyproject.toml, no model files - torch and numpy are all it needs, and both are already there in any ComfyUI install.
Where people get burned
Turning supersample down to 1 to "save time" is the mistake I'd warn against first. This node does a single bilinear resample of fine type; at 1× the label text shimmers, and the whole reason it looks convincing is that the text is antialiased before the warp. It's cheap at 2×.
edge_fade_start at or above edge_fade_end gives you no falloff at all, and the label terminates on the silhouette with a visible straight cut. Keep the pair ordered and roughly a fortieth of the radius apart.
And broadly: this node wants the aligned, padded, fixed-frame image. Feed it anything hand-resized and you're recalibrating six coupled fractions against a canvas that will change next run. That's the pack's logic - pad and align first - and skipping it costs you here.
Inputs (12)
| Name | Type | Default | Description |
|---|---|---|---|
| label | IMAGE | — | |
| coverage | MASK | — | |
| reference | IMAGE | — | |
| center_x | FLOAT | 0.4990-1–2 | — |
| radius | FLOAT | 0.21320.01–1 | — |
| top | FLOAT | 0.5137-1–2 | — |
| bottom | FLOAT | 0.8531-1–2 | — |
| u_center | FLOAT | 0.2796-1–2 | — |
| u_scale | FLOAT | 1.00000.5–2 | — |
| edge_fade_start | FLOAT | 0.9300.5–1 | Fraction of the radius where the label starts handing over to the base image edge. |
| edge_fade_end | FLOAT | 0.9750.5–1 | — |
| supersample | INT | 21–3 | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| label | IMAGE | — |
| mask | MASK | — |