Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The HUD

Verified against Minecraft 26.2 · Part X · press F1 and the interface goes away — except for the thing that can black out your whole screen.

Hud.extractRenderState is a single ordered method wrapped in two hidden-gated blocks, and one element sits between them: the sleep fade. So F1 hides the hearts, the hotbar, the crosshair, chat and the tab list, and does not hide the black screen that comes over you when you get into bed. Four more elements are recorded by Gui, after the screen rather than with the rest of the HUD — the saving indicator, the debug overlay, the deferred subtitles and the toasts — and only the toasts are outside Hud’s own methods. Only the saving indicator ignores the flag.

That is what this page is: not a tour of hearts and hotbars but a policy. What is drawn over the world, in what order, under exactly which conditions, and which of those conditions is a surprise. The full per-element table is what the HUD draws, and when in Reference; the machinery underneath every one of them is the GUI render tree.

The cast

classwhat it decidesthread
Hudthe order, the two hidden-gated blocks, and the health animation stateRender thread
Guithe four elements recorded after the screenRender thread
ContextualBarwhich of four things occupies the one slot above the hotbarRender thread
BossHealthOverlaythe bars, and three questions the world renderer asks itRender thread
ChatComponentthe message list, its wrapped lines, and what is faded outRender thread
DebugScreenEntriesthe F3 registry: what an entry is, and whether it is onRender thread
DebugScreenEntryListthe per-entry status, its presets, and its own save fileRender thread
GuiGraphicsExtractoreverything the HUD records intoRender thread

The order, and the two blocks

flowchart TD
    PUB["publish GuiRenderState.isHudHidden — before any check, so the flag is always current"]
    LLS{"is a LevelLoadingScreen up?"}
    STOP["record nothing"]
    H1{"hidden?"}
    A["camera overlays, then the crosshair, then a new stratum, then the hotbar block, effects, boss bars"]
    SLEEP["the sleep fade — ungated"]
    H2{"hidden?"}
    B["demo text, scoreboard sidebar, action bar, title, chat, tab list, subtitles"]
    B2["subtitles only, and only if an in-game-UI screen is up"]
    GUI["Gui continues: saving indicator, toasts, debug overlay, deferred subtitles"]
    PUB --> LLS
    LLS -- "yes" --> STOP
    LLS -- "no" --> H1
    H1 -- "no" --> A --> SLEEP
    H1 -- "yes" --> SLEEP
    SLEEP --> H2
    H2 -- "no" --> B --> GUI
    H2 -- "yes" --> B2 --> GUI

Two structural facts follow from the shape. The hidden flag is published before the loading-screen short-circuit, so the renderer’s copy is correct even on a frame where the HUD records nothing. And toasts and the debug overlay are always above a screen, because Gui records them after it — while the deferred subtitles are called from a screen’s background pass and therefore land under the screen’s widgets.

Hud.tick runs from Gui.tick once per client tick and takes a pause flag: the autosave indicator animates either way, everything else only when the game is not paused.

The hidden flag travels two ways

The interesting one is the smaller. GuiRenderState.isHudHidden is read by GameRenderer in three places, to suppress the held item, the three-dimensional crosshair and — the smallest of the three — the totem-pop animation. The block-in-eyes, water and fire overlays are drawn whatever F1 says. So a 2D flag does change how the world is drawn, in three narrow ways. But Hud.isHidden itself is read directly by six other places across the client, including two entity renderers that suppress name tags.

Four states, one slot, and an asymmetric rule

Hud.contextualInfoBar holds one of four states — nothing, experience, the locator, a jumpable vehicle — and Hud.nextContextualInfoState re-decides which every frame. It is a rule, not a state machine, and the rule is not symmetric. With waypoints present, a jumping vehicle or a recently changed experience total beats the locator; with no waypoints, a jumpable vehicle beats experience unconditionally — so mounting a horse silently takes your XP bar away. The level number is recorded separately from the bar, so it survives whichever bar wins.

The three implementations are ExperienceBar, LocatorBar — backed by ClientWaypointManager and styled by Hud.waypointStyles, a WaypointStyleManager that is the HUD’s own reload listener — and JumpableVehicleBar. Hud.ContextualInfo is the enum of the four states.

The hearts, which are three numbers at once

sequenceDiagram
    participant CPL as ClientPacketListener
    participant LP as LocalPlayer
    participant Hud as Hud
    participant GGE as GuiGraphicsExtractor

    CPL->>LP: handleSetHealth — hurtTo, which sets hurtTime and invulnerableTime
    Note over Hud: next frame
    Hud->>Hud: extractPlayerHealth — health fell while invulnerable
    Hud->>Hud: healthBlinkTime becomes tickCount plus 20 (a heal sets 10)
    Hud->>Hud: displayHealth catches up only once the second has elapsed
    Hud->>Hud: random.setSeed(tickCount times a constant) — the jitter is per tick, not per frame
    Hud->>Hud: extractHearts — one descending pass: container, absorption, ghost, truth
    Hud->>GGE: blitSprite per heart, chosen by HeartType.getSprite

The shake is seeded from the tick counter, so it jitters at 20 Hz and is identical across two frames of the same tick — and the same seeded stream drives the hunger jitter and the air-bubble wobble, which is why they shake together. The blink is a square wave with a three-tick half-period, running for twenty ticks after damage and ten after a heal, and what it draws is the ghost: the HUD keeps three health numbers at once — last frame’s, a lagging display value that catches up about once a second, and the truth — and the blinking layer shows the one that is out of date on purpose.

Hud.HeartType is six constants, one of which Mojang spells Hud.HeartType.POISIONED, each with eight sprites.

One gate silences four elements at once: armour, hearts, food and air are all recorded inside Hud.extractPlayerHealth, which is gated on the game mode being able to hurt you — which is why creative has no armour bar either. Food and mount health share a slot, and the air bubbles shift up when either is drawn. And the HUD makes a sound: Hud.playAirBubblePoppedSound ramps its volume and pitch with how many bubbles are gone, so it climbs as you drown.

Questions players ask

Why does the boss bar change the sky? The overlay answers three questions — should the screen darken, should world fog be created, should the End music play — read from five places between the frame, the fog environment and the lightmap. The bar itself interpolates against wall-clock time inside LerpingBossEvent, so discrete packet progress becomes a smooth bar.

Why do subtitles appear under an open chest? They are deferred past the screen, along with the tooltip and the pre-edit overlay the extractor holds. The deferral fires when there is no screen at all or the screen declares itself in-game UI — the common case, not the rare one — and the deferred call is made from a screen’s background pass.

Are the chat HUD and the chat screen the same thing? The same code in different modes. The HUD bails out entirely when the chat screen is focused. Message age is measured in HUD ticks rather than timestamps, and a message the server asked to delete keeps its original timestamp when it is replaced by a marker — so the marker fades on the original message’s schedule. ChatComponent holds four collections, not two: every message and every wrapped line are separate lists with separate caps, and the deletion queue and the recent-input history are the other two. A fifth, the delay-option queue, lives on ChatListener. Signing belongs to chat and signing; this page owns display only.

Is the pumpkin blur hardcoded? No. The camera overlay list is data-driven: every equipment slot is asked whether its item declares a camera overlay.

Can I turn a debug line on without pressing F3? Yes, and the game saves that you did. DebugScreenEntries holds every entry by Identifier, each a DebugScreenEntry writing lines through a DebugScreenDisplayer; DebugScreenEntryList, reachable as Minecraft.debugEntries, stores a DebugScreenEntryStatus per entry, ships DebugScreenProfile presets and persists to its own file with its own data-fixer type. An entry set to always-on renders with F3 never pressed. The screen that edits it is suppressed by Gui, not by the overlay. The charts are FpsDebugChart, TpsDebugChart, PingDebugChart and BandwidthDebugChart over AbstractDebugChart — plus ProfilerPieChart, which is not one of them.

Why is that F3 shortcut not rebindable? Twenty are ordinary key mappings; a second family is a raw switch on key codes behind the game’s debug flag, bindable to nothing. Several of the mappings toggle debug entries that print nothing at all and exist only to carry a flag the world renderer reads.

What counts as “HUD state”? Whatever Hud.onDisconnected resets — tab list, boss bars, toasts, debug overlay, chat and titles, together. It is the only place that clears the HUD as a whole, and it is a better definition than any list of fields.

For a 1.21-era reader. Gui is not the HUD any more. The class that drew the hotbar and hearts is Hud; the name Gui was reused for the screen and overlay manager that used to be fields on Minecraft. The canonical path is Gui.hud. Every render* method on Gui is now an extract* on Hud, and gone with them: LayeredDraw, Minecraft.screen, Minecraft.getToastManager, Minecraft.fpsString, Options.hideGui, and DebugScreenOverlay.render along with its two information-gathering methods — the line content moved out into the entry registry.

Where to look

Hud.extractRenderState — the whole HUD is one ordered method, and the two hidden-gated blocks are visible at a glance. Then Hud.extractPlayerHealth for the most-loved fifty-seven lines in the client, Hud.nextContextualInfoState for the bar arbitration, Gui.extractRenderState for the four recorded after the screen, DebugScreenEntries for the F3 registry, and ChatComponent for the message list.


Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes tools/verify_names.py.