window_create and window_control¶
Two effects that open and drive separate OS windows: window_create opens a named picture
window and holds it for its own lifetime, window_control moves, animates and retitles that window
by name. They are not post-processing passes and not world geometry - nothing about the Minecraft
window changes unless the window id is the reserved "0", which drives the Minecraft window itself
(see the reserved id below).
type: "window_create" / type: "window_control"
What you actually get¶
A window_create opens a frameless, transparent, click-through picture window over the desktop:
- No frame, ever. The window is undecorated and non-resizable: no title bar, no border, no OS drag or maximize. There is no field to turn a frame on - the picture is the whole feature.
- Clicks pass through. The window is
GLFW_MOUSE_PASSTHROUGHat all times, so the game (and everything else under it) keeps receiving input. It is not a field and cannot be turned off: a floating full-screen window that swallowed clicks would block the desktop. - Focus stays with the game. A window that took focus when it appeared would alt-tab the player out of Minecraft the moment an effect started; focus-on-show is disabled and the window is never focused.
- Always on top, so the picture sits over the game rather than behind it.
- Transparent background (the picture composites over whatever is under it) where the platform supports it; the fallback is an opaque black canvas, which is a no-op you cannot switch off.
The canvas¶
The window is created hidden, sized to the whole work area of the monitor the game window is on and shown last; it is never moved or resized afterwards. Everything you animate - position, size - is the picture's rectangle inside that canvas, so animation costs no OS call per frame and cannot stutter. Two consequences worth knowing:
- The monitor is pinned at creation.
pos_*/size_*always mean fractions of the work area of the monitor the game window was on when the window opened. Drag the game to another monitor or change the taskbar, and the window keeps its original canvas (stop and replay the effect to re-pin it). size = 1is "fullscreen". There is no OS fullscreen path: filling the canvas is what fullscreen means here.
On Wayland the compositor owns window placement and sizing, so the canvas may not cover the
area you expect and absolute pos_* positioning is best-effort. Windows-primary is the supported
path.
Fields¶
window_create and window_control take three top-level fields (next to type, never inside
params), because they are strings and the window's identity does not animate:
| Field | Type | Default | Meaning |
|---|---|---|---|
id |
string | — (required) | The window's name - the key window_control addresses it by. Non-blank; anything may be used ("hud", "vfx:hud") except the reserved "0", which drives the Minecraft window |
texture |
string | — | Resource id of the picture ("vfx_demos:window/pic"), resolved from the client's resource manager, so a resource pack can supply it. A missing .png suffix is added. Omit it for a window with no picture (only a title). A mod may instead supply the picture from code with VFXAPI.registerImage(id, …) and reference that same id here (see the API) |
titles |
array of strings | [] |
Candidate OS titles; title_index picks one. At most 64, every entry a string |
Everything else is an ordinary param, so it takes the whole
param spec surface - constant, start/end, keyframes, bind, expr,
the /vfx play param-map and live setParam:
| Param | Type | Default | Meaning |
|---|---|---|---|
pos_x |
float | 0 | Left edge, 0..1 of the free work-area space (see below) |
pos_y |
float | 0 | Top edge, 0..1 of the free work-area space |
size_w |
float | 1 | Picture width, 0..1 of the smaller work-area side. size_w : size_h are the picture's own proportions, not fractions of the monitor's axes (see below) |
size_h |
float | 1 | Picture height, 0..1 of the smaller work-area side |
opacity |
float | 1 | OS window opacity, 0..1 - animate it for a fade |
frames |
float | 1 | How many frames the picture is split into, at least 1. Read once, when the window opens; 1 = a still image. Ignored when the PNG has a sibling .mcmeta (see below) |
frame_time |
float | 0 | Ticks per frame for a PNG without a .mcmeta. 0 (the default) means no stepping - the picture holds frame 0 |
title_index |
float | 0 | Which entry of titles the OS title shows |
Notes on the numbers, because the defaults are not the intuitive ones:
pos_*is relative to the free space, not to the work area.size_*is applied first, thenpos_*is mapped over what is left:x = workArea.x + pos_x * max(0, workArea.w - width). Sopos_x: 1puts the picture flush against the right edge whatever its width, andpos_x: 0.5centres it.pos_*andsize_*are clamped into0..1, so an overshooting keyframe cannot push the picture off the canvas.size_w : size_hare the picture's own proportions, not the monitor's. Both values are taken in one common unit (the smaller work-area side), sosize_w == size_his a square on any screen and a deliberate difference stretches the picture - the monitor's aspect never distorts the shape. The picture fills that box (there is no aspect fit of the source), sosize_w:size_his exactly the on-screen width:height and you can animate a stretch by driving the two out of step.size_h = 1is as tall as the smaller screen dimension; the values are clamped into0..1.- A resource-pack PNG with a sibling
.mcmetaanimates by the vanilla format. If thetextureresolves to a pack file that has a.png.mcmetanext to it (the vanilla animation format: optionalframeWidth/frameHeight, aframeslist with per-frametime,defaultFrameTime), the window plays that animation - the sheet is the whole grid (multi-row is fine), each frame shows for its own time (milliseconds, rounded to ticks, at least one) and the loop wraps. For that pictureframesandframe_timeare ignored.interpolate: trueis not supported - frames switch without blending. framessplits the image into a horizontal strip (a PNG without a.mcmeta). The sheet is read row-major with frame 0 top-left, which for a strip means the image is split intoframesequal columns left to right. The PNG must therefore be at leastframespixels wide; a narrower one is refused with a one-time warning and the window keeps its last good picture.framesis read at creation, so animating it does nothing - change it and replay the effect. Anyframe_indexthe animation produces is wrapped, so it never draws outside the sheet.frame_timeis in ticks (20 ticks = 1 s), soframe_time: 4is 5 fps and2is 10 fps. It is independent of the effect'sdurationandeasing: it counts real elapsed ticks. It applies only to a PNG without a.mcmeta.title_indexis rounded and clamped.0.6picks entry 1, a negative index picks the first title and an index past the end picks the last, so an out-of-range value never fails - keyframes can step through the list without a bounds check. The index is read from thewindow_createand every livewindow_controlfor that name, in effect order.
Window names, and who owns them¶
- The window lives while its
window_createis active. Adurationeffect closes it at expiry, apersistent/loopone keeps it until/vfx stop, a world exit closes it, and quitting closes it. - First creator wins. If a second
window_createplays for a name that is already open, it warns once and does nothing - the running window is never replaced under its owner. Stopping the first creator frees the name; replaying then opens a fresh window. window_controlneeds the window. On a name with no open window it is a no-op with a one-time warning.- Two
window_controlon one window are both applied, in the order the effects are active, last writer wins - so a control can be layered on a creator's geometry without either erroring. - At most 8 windows at once. A ninth is dropped with a one-time warning; existing windows are never evicted to make room.
The reserved id "0": the Minecraft window itself¶
The id "0" is reserved. A window_create or window_control with that id does not open a
picture window: it drives the Minecraft game window itself - its size and its position on the
screen. Everything else in this page still applies to every other id, unchanged.
{
"type": "window_create",
"id": "0",
"persistent": true,
"params": {
"pos_x": 0.5,
"pos_y": 0.5,
"size_w": { "start": 1.0, "end": 0.5 },
"size_h": { "start": 1.0, "end": 0.5 }
}
}
What is different for id "0"¶
- The geometry is the window's own rect, not a picture inside a canvas.
size_w/size_hare 0..1 of the monitor work area andpos_x/pos_yare the centre of the window as a 0..1 fraction of the work area - so any resize grows and shrinks evenly about that centre and never pins a corner.size = 1is a maximized-looking window filling the work area,pos_x: 0.5, pos_y: 0.5is dead centre, andpos_x: 0.5, size_w: 0.5is the middle half of the screen. - The work area is pinned when the
window_createstarts, exactly like an aux canvas: it is the work area of the monitor the game window was on at that moment. Replay the effect to re-pin it. - Only an effect that declares a geometry moves the window. If neither the
window_createnor any livewindow_controlfor"0"mentionspos_x/pos_y/size_w/size_h(in the definition or through a live/vfx set), the window is left completely alone - a"0"effect that only renames or fades something is a no-op on the window. - Fullscreen is left first. While the game window is in exclusive fullscreen, an id-
"0"effect that declares a geometry switches it back to windowed at the requested size (through Minecraft's own fullscreen path, so the game's own F11 state stays correct) and then moves it. - The window stays an ordinary Minecraft window. No frame is removed, it is not made click-through,
transparent, always-on-top, resizable-disabled or undecorated: it is the normal window you play in.
There is no picture, no OS title and no opacity for
"0"-texture,titles,frames,frame_time,title_indexandopacityare ignored, because the game window has no picture surface to draw into and its title and decorations are yours. - Stopping leaves it where it is. When the driving effect stops, expires, the world is left or the game quits, the window is not moved or resized back: it stays at the size and position the effect left it, and you can move or resize it yourself. (A jump back to the pre-effect rect would read as a bug, and the mod has no idea what you had before the effect started.)
- Never 0x0. The size is clamped up to at least 320x240 and down into the work area, so an
over-small or negative
size_*still leaves a window the game can render into. - Resizing the game window interrupts chat input. An animated
size_w/size_hchanges the OS window size every frame, and resizing the window repeatedly drops the chat screen's key handling: the command history keys (Up/Down) and tab-completion can stop responding while the resize runs (and briefly after). This is a consequence of driving the real OS window, not of the effect logic. Prefer id-"0"size animation where nobody is typing, or hold the size steady and animate only the position; either way it recovers by reopening the chat. - On Wayland the compositor owns window placement, so the position is best-effort there too.
The geometry is driven from the render thread after the game's own present, once per frame, and only the position is changed when it actually differs - so an idle id-
"0"effect costs two integer reads per frame and no OS call at all.
Two things that are quirks, not bugs¶
fade_tickson the creator pops the window instead of fading it. A stopping creator closes its window the moment the fade begins, so the picture disappears at once. Use anopacityramp (keyframes orstart/end) if you want it to fade out.- F3+T does not refresh the picture. The image is decoded and uploaded once, when the window
opens; a resource reload leaves the old picture on screen until the window is recreated. Recreate it
(stop and replay the
window_create).
Honest limits¶
- Client-side. The window belongs to the client the effect is active on: a server-triggered
window effect opens one window on every client playing it.
title_index,opacityand the geometry are ordinary params and can be driven from the server like any other;textureandtitleslive in the definition, which is synced like every other definition. - Per-node capability. Each Minecraft line ships its own GLFW: click-through needs GLFW 3.4, transparency and no-focus-on-show need 3.3. Where a node's GLFW is older the feature is simply not used (an opaque canvas, or a window that can take focus) - never an error, never frame damage.
- No pass is added to the game frame. With no window effect active, nothing here runs at all; the frame is byte-for-byte normal. The aux windows are presented after the game's own present, through their own GL contexts, with vsync-waiting disabled so they cannot slow the game down.
- No
decoratedand nofullscreenfield: the frame cannot be enabled and fullscreen issize = 1.
Example¶
A picture window with a 4-frame strip and three titles, opened on one monitor's work area, and a control that slides it, retitles it and fades it:
{
"type": "window_create",
"id": "hud",
"texture": "vfx_demos:window/logo",
"titles": ["VFX Weaver", "ready", "charging"],
"persistent": true,
"fade_ticks": 8,
"params": {
"pos_x": 0.5,
"pos_y": 0.05,
"size_w": 0.2,
"size_h": 0.2,
"opacity": 1.0,
"frames": 4,
"frame_time": 5,
"title_index": 0
}
}
{
"type": "window_control",
"id": "hud",
"persistent": true,
"params": {
"pos_x": { "start": 0.5, "end": 0.75 },
"size_w": { "start": 0.2, "end": 0.35 },
"opacity": { "keyframes": [
{ "time": 0, "value": 1.0 },
{ "time": 40, "value": 0.2 },
{ "time": 80, "value": 1.0 }
] },
"title_index": { "keyframes": [
{ "time": 0, "value": 1 },
{ "time": 40, "value": 2 }
] }
}
}
/reload
/vfx play vfx_demos:window_hud
/vfx play vfx_demos:window_hud_move
/vfx play vfx_demos:window_hud {[opacity:0.5]}
Author both effects in your own namespace (vfx_demos: above) - a pack that writes
vfxweaver:window_hud would shadow a built-in rather than add one.
Code¶
The window subsystem is dev.vfxweaver.client.window (canvas window, picture content, controller,
registry, lifecycle manager, the game-window driver for the reserved id and the per-node platform
gate). It is asserted by
scripts/check-window-platform.ps1, check-window-canvas.ps1, check-window-content.ps1,
check-window-registry.ps1, check-window-control.ps1, check-window-fields.ps1,
check-window-lifecycle.ps1 and check-game-window.ps1. Not verified in game - the pixels, the passthrough, the focus behaviour and the game-window geometry.
Showcase¶
A picture window (the ender-eye sprite) and the reserved "0" game window driven at the same time:
the sprite window travels to each screen edge while the Minecraft window itself pulses in size and
position. Each is one datapack effect.
The picture window (a named id)¶
The game window (the reserved id "0")¶
See also¶
- Datapack format - definition fields and structural blocks.
- Animating a param - keyframes,
start/end,expr,bind. - surface_pattern - the same sprite-sheet addressing on the terrain.
- Commands -
/vfx play,/vfx stop, the param-map syntax.