Modscraper Modscraper Minecraft
Hudifine logo

Hudifine

Mod

Build custom HUD overlays with a simple scripting language. No mod coding knowledge needed.

Type

Mod

Modrinth Downloads

24

Modrinth ID

Feip4LDZ

Last Updated

May 28, 2026

Description

Hudifine is an HUD mod that allows anyone to make HUDs easily via HUD Script, which displays the metrics you choose.

Open the chat up to add and configure them.

Below is a documentation for HUDScript, the scripting language of Hudifine.

HUD Script Developer Documentation

HUD Script is a safe, sandboxed scripting format for creating fully customizable in-game HUD overlays in Minecraft (Fabric). Scripts are paste-and-run; no mod dev environment required.

Table of Contents

  1. Overview
  2. Script Structure
  3. Screen Budget (30% Rule)
  4. Data Sources
    • Player
    • World
    • Performance
    • Combat
    • Inventory
    • Input
    • Environment
    • Game State
    • Multiplayer
    • Time
  5. Layout System
    • Ordering & Layering
  6. Styling
  7. Elements
  8. Conditionals & Logic
  9. Events
  10. Accessibility Toggles
  11. Player Settings API
  12. Animations
  13. CPS Implementation Guide
  14. Full Examples

Overview

HUD Script is a declarative + event-driven scripting format. Scripts define:

  • What data to display (from a fixed set of allowed data sources)
  • How to display it (full visual freedom within the screen budget)
  • Player-configurable settings (accessibility, toggles, colors, etc.)

Scripts are sandboxed by design. They can only:

  • ✅ Read from allowed data sources
  • ✅ Render to the HUD overlay
  • ✅ React to player input visually
  • ❌ Make network requests
  • ❌ Access the file system
  • ❌ Interact with anything outside the widget

Script Structure

Every script has three optional top-level blocks:

meta { name: "My Widget" author: "yourname" version: "1.0.0" description: "A short description of what this widget does." } settings { // Player-configurable options exposed in the right-click menu } widget { // Layout, elements, styling, logic }

Only the widget block is required.

Screen Budget (30% Rule)

A widget may occupy no more than 30% of the player's screen area at any time.

  • Screen area is calculated as screenWidth × screenHeight
  • Your widget's bounding box (all elements combined) must not exceed screenWidth × screenHeight × 0.30
  • This is enforced at runtime — if your widget exceeds 30%, it will be scaled down automatically and a warning will appear in the widget editor
  • Widgets that are toggled off or fully transparent do not count toward the budget
  • Multiple widgets each have their own 30% budget independently

Tip: Use the size property wisely and test at different resolutions. Design for 1920×1080 as your baseline.

Data Sources

All data sources are read-only. Call them inside element values using get().

Player

Source Returns Description player.health float Current health (0.0–20.0) player.maxHealth float Max health including absorption player.absorption float Absorption hearts player.hunger int Food level (0–20) player.saturation float Saturation level (0.0–20.0) player.exhaustion float Exhaustion value (0.0–4.0) player.air int Air supply ticks (0–300) player.xp int Total XP points player.xpLevel int Current XP level player.xpProgress float XP progress to next level (0.0–1.0) player.speed float Current movement speed (blocks/sec) player.isSprinting bool Whether player is sprinting player.isSneaking bool Whether player is sneaking player.isSwimming bool Whether player is swimming player.isFlying bool Whether player is flying (creative/elytra) player.isFalling bool Whether player is falling player.isOnGround bool Whether player is on the ground player.isInWater bool Whether player is in water player.isInLava bool Whether player is in lava player.fallDistance float Current fall distance in blocks player.reachDistance float Current reach distance player.name string Player's username player.uuid string Player UUID player.gamemode string survival, creative, adventure, spectator player.score int Player's current scoreboard score player.ping int Latency in ms (multiplayer only)

World

Source Returns Description world.x float Player X coordinate world.y float Player Y coordinate world.z float Player Z coordinate world.blockX int Player block X (floored) world.blockY int Player block Y (floored) world.blockZ int Player block Z (floored) world.chunkX int Current chunk X world.chunkZ int Current chunk Z world.facing string Cardinal direction: N, NE, E, SE, S, SW, W, NW world.facingDegrees float Yaw in degrees (0–360) world.pitch float Camera pitch (-90 to 90) world.yaw float Camera yaw (0–360) world.dimension string overworld, nether, end, or custom namespace world.biome string Current biome name (e.g. minecraft:plains) world.lightLevel int Block light level at player feet (0–15) world.skyLightLevel int Sky light level at player position (0–15) world.moonPhase int Moon phase (0–7) world.isRaining bool Whether it is raining world.isThundering bool Whether there is a thunderstorm world.rainStrength float Rain intensity (0.0–1.0) world.difficulty string peaceful, easy, normal, hard world.name string World/server name world.seed string World seed (singleplayer only, returns "hidden" on servers) world.spawnX int World spawn X world.spawnZ int World spawn Z world.distanceToSpawn float Distance to world spawn in blocks

Performance

Source Returns Description perf.fps int Current frames per second perf.fpsAvg float Average FPS over last 5 seconds perf.fpsMin int Minimum FPS over last 5 seconds perf.fpsMax int Maximum FPS over last 5 seconds perf.frameTime float Last frame time in milliseconds perf.frameTimeAvg float Average frame time over last 5 seconds perf.tps float Server ticks per second (multiplayer only) perf.mspt float Milliseconds per tick (server, multiplayer only) perf.chunkUpdates int Chunk updates this frame perf.renderedChunks int Number of rendered chunks perf.entities int Entities in render distance perf.blockEntities int Block entities in render distance perf.particles int Active particles

Combat

Source Returns Description combat.attackCooldown float Attack cooldown progress (0.0–1.0) combat.attackCooldownMs int Milliseconds until full attack cooldown combat.isAttacking bool Whether player is currently attacking combat.lastDamage float Last damage received combat.lastDamageSource string Source of last damage (e.g. fire, fall, player) combat.killCount int Player kills this session combat.deathCount int Player deaths this session combat.kdr float Kill/death ratio this session combat.targetName string Name of entity player is looking at (if any) combat.targetHealth float Health of entity player is looking at combat.targetMaxHealth float Max health of entity player is looking at combat.targetDistance float Distance to targeted entity combat.targetType string Type of entity player is looking at combat.isInCombat bool Whether player has been hit/hit something recently combat.combatTimer int Seconds since last combat action

Inventory

Source Returns Description inventory.hotbarSlot int Current hotbar slot (0–8) inventory.mainHandItem string Item ID in main hand inventory.mainHandCount int Stack count in main hand inventory.mainHandDurability int Durability of main hand item inventory.mainHandMaxDurability int Max durability of main hand item inventory.mainHandDurabilityPct float Durability % of main hand item (0.0–1.0) inventory.offHandItem string Item ID in off hand inventory.offHandCount int Stack count in off hand inventory.offHandDurability int Durability of off hand item inventory.helmetItem string Item ID of helmet inventory.helmetDurability int Durability of helmet inventory.helmetDurabilityPct float Durability % of helmet (0.0–1.0) inventory.chestplateItem string Item ID of chestplate inventory.chestplateDurability int Durability of chestplate inventory.chestplateDurabilityPct float Durability % of chestplate (0.0–1.0) inventory.leggingsItem string Item ID of leggings inventory.legginsDurability int Durability of leggings inventory.leggingsDurabilityPct float Durability % of leggings (0.0–1.0) inventory.bootsItem string Item ID of boots inventory.bootsDurability int Durability of boots inventory.bootsDurabilityPct float Durability % of boots (0.0–1.0) inventory.arrowCount int Number of arrows in inventory inventory.totalSlots int Total inventory slots (36) inventory.usedSlots int Number of filled inventory slots inventory.emptySlots int Number of empty inventory slots inventory.xpBottleCount int XP bottles in inventory

Input

Input sources reflect what the player is physically pressing. They are display-only; you cannot intercept, block, or redirect input.

Source Returns Description input.forward bool W key held input.backward bool S key held input.left bool A key held input.right bool D key held input.jump bool Space held input.sneak bool Shift held input.sprint bool Sprint key held input.attack bool Left mouse button held input.use bool Right mouse button held input.drop bool Q key held input.inventory bool Inventory key held input.swap bool F key (swap hands) held input.hotbar1–input.hotbar9 bool Hotbar slot keys held input.cps int Left click CPS (clicks per second) input.rcps int Right click CPS input.mouseX int Mouse X position on screen input.mouseY int Mouse Y position on screen input.mouseDeltaX float Mouse X movement this frame input.mouseDeltaY float Mouse Y movement this frame input.sensitivity float Current mouse sensitivity (0.0–1.0)

Environment

Source Returns Description env.timeOfDay int World ticks (0–24000) env.timeString string Formatted time (e.g. 6:00 AM) env.dayCount int In-game days elapsed env.temperature float Biome temperature at player position env.humidity float Biome humidity at player position env.canSeeSky bool Whether player has line of sight to sky env.isUnderground bool Whether player is underground (no sky access) env.nearestVillageDistance float Distance to nearest detected village (blocks) env.isInVillage bool Whether player is inside a village env.nearestPlayerDistance float Distance to nearest other player (multiplayer) env.nearestPlayerName string Name of nearest other player

Game State

Source Returns Description game.isPaused bool Whether the game is paused game.isInGui bool Whether any GUI is open game.isInventoryOpen bool Whether inventory is open game.isInBed bool Whether player is sleeping game.isRiding bool Whether player is riding an entity game.ridingEntityType string Type of entity being ridden game.ridingEntityHealth float Health of entity being ridden game.isElytraFlying bool Whether player is elytra flying game.elytraHealth int Current elytra durability game.elytraSpeed float Current elytra speed (blocks/sec) game.potionEffects list<string> Active potion effect IDs game.potionEffectAmplifiers map<string, int> Amplifier per active effect game.potionEffectDurations map<string, int> Duration ticks per active effect game.scoreboardObjective string Current sidebar scoreboard objective name game.scoreboardScore int Player's score on current sidebar objective game.bossBarName string Name of active boss bar (if any) game.bossBarProgress float Boss bar progress (0.0–1.0) game.isSpectatingEntity bool Whether player is spectating an entity game.spectatingEntityType string Type of entity being spectated

Multiplayer

Source Returns Description server.name string Server name / IP server.motd string Server MOTD server.playerCount int Current online player count server.maxPlayers int Server max player capacity server.ping int Ping to server in ms server.tps float Server TPS server.isLAN bool Whether connected to LAN server server.isSingleplayer bool Whether in singleplayer server.version string Server Minecraft version

Time

Source Returns Description clock.hour int Real-world hour (0–23) clock.minute int Real-world minute (0–59) clock.second int Real-world second (0–59) clock.timeString string Formatted real time (e.g. 14:32) clock.timeString12 string 12-hour format (e.g. 2:32 PM) clock.sessionTime int Seconds in current play session clock.sessionTimeString string Formatted session time (e.g. 1h 23m) clock.date string Real-world date (e.g. 2025-04-26) clock.unixTimestamp int Unix timestamp (seconds)

Layout System

Widgets use a simple box-based layout anchored to screen positions.

Anchor Points

widget { anchor: top-left // top-left, top-center, top-right // middle-left, center, middle-right // bottom-left, bottom-center, bottom-right offsetX: 10 // Pixel offset from anchor X (can be negative) offsetY: 10 // Pixel offset from anchor Y (can be negative) }

Sizing

widget { width: 120 // Fixed pixel width height: auto // "auto" sizes to content, or fixed pixels padding: 8 // Uniform padding (or padding: 8 4 for vertical/horizontal) margin: 4 // Outer margin }

Direction

widget { direction: column // "column" (vertical stack) or "row" (horizontal stack) gap: 4 // Gap between child elements in pixels align: start // "start", "center", "end" — cross-axis alignment justify: start // "start", "center", "end", "space-between" — main axis }

Z-Index

widget { zIndex: 10 // Higher values render on top of other widgets }

Ordering & Layering

By default, elements render in the order they are written in the script, top to bottom. You can override this with the order property on any element or group.

widget { direction: column text { value: "I appear second" order: 2 color: #ffffff fontSize: 12 } text { value: "I appear first" order: 1 color: #aaaaaa fontSize: 10 } text { value: "I appear third" order: 3 color: #555555 fontSize: 9 } }

order works like CSS order; lower numbers render first (higher up or further left depending on direction). Elements without an order value default to order: 0 and render before explicitly ordered ones.

You can also use order inside a group:

group { direction: row text { value: "CPS" order: 2 color: #aaaaaa fontSize: 9 } keyIndicator { key: input.attack label: "LMB" order: 1 size: 20 borderRadius: 8 activeColor: #ffffff inactiveColor: #ffffff33 } }

Tip: Use order when you want your script logic grouped together but the visual layout to differ. If everything has an explicit order, it's cleaner to just rewrite the script in the correct order instead.

Styling

Background

widget { background: #00000088 // Hex color with optional alpha (last 2 digits) background: transparent // No background borderRadius: 6 // Rounded corners in pixels border: 1 #ffffff44 // Border: width color backdropBlur: 4 // Blur pixels behind widget (GPU-dependent) }

Text Styling

text { value: get(player.health) color: #ffffff fontSize: 12 // In pixels (8–32 recommended) fontWeight: bold // "normal", "bold" fontStyle: normal // "normal", "italic" shadow: true // Minecraft-style text shadow shadowColor: #00000066 letterSpacing: 0 // Pixels between characters lineHeight: 1.2 // Line height multiplier align: left // "left", "center", "right" truncate: 20 // Max characters before truncation (optional) uppercase: false // Force uppercase }

Colors

Colors are hex strings: #RRGGBB or #RRGGBBAA (with alpha).

color: #ff0000 // Red, fully opaque color: #ff000088 // Red, 50% transparent color: #fff // Shorthand (expands to #ffffff)

Opacity

widget { opacity: 0.8 // 0.0 (invisible) to 1.0 (fully opaque) }

Elements

Elements are the visual building blocks inside a widget block.

text

Displays a string value.

text { value: "FPS: {get(perf.fps)}" // Inline expressions with {} color: #ffffff fontSize: 12 shadow: true }

bar

A fill bar (health, XP, cooldown, etc.)

bar { value: get(player.health) max: get(player.maxHealth) width: 100 height: 8 fillColor: #ff4444 backgroundColor: #00000066 borderRadius: 4 direction: left-to-right // "left-to-right", "right-to-left", "bottom-to-top", "top-to-bottom" }

icon

Renders a Minecraft item or block icon.

icon { item: get(inventory.mainHandItem) // Item ID string size: 16 // Icon size in pixels }

circle

A circular progress indicator.

circle { value: get(combat.attackCooldown) // 0.0–1.0 radius: 12 strokeWidth: 3 color: #ffffff backgroundColor: #ffffff33 startAngle: -90 // Degrees — 0 = right, -90 = top direction: clockwise // "clockwise" or "counterclockwise" }

image

Renders a static texture from the resource pack.

image { texture: "hud:textures/my_icon.png" // Namespace:path within resource pack width: 16 height: 16 tint: #ffffff // Optional color tint }

keyIndicator

Displays a visual key that lights up when pressed.

keyIndicator { key: input.forward label: "W" activeColor: #ffffff inactiveColor: #ffffff44 size: 20 // Width and height of the key in pixels width: 20 // Override width only (useful for SPACE, LMB, RMB) borderRadius: 8 // Higher = more rounded. Use 999 for a full pill shape fontSize: 9 // Label font size inside the key shadow: true // Text shadow on label }

Tip: For wide keys like SPACE, LMB, and RMB, set width larger than size to stretch them horizontally while keeping the same height. A borderRadius of 8–12 matches the Lunar Client look.

group

Container to group and nest elements together.

group { direction: row gap: 4 background: #00000066 padding: 6 borderRadius: 4 text { value: "Health: " color: #aaaaaa fontSize: 11 } text { value: "{get(player.health)}" color: #ff4444 fontSize: 11 } }

separator

A horizontal or vertical divider line.

separator { direction: horizontal // "horizontal" or "vertical" color: #ffffff22 thickness: 1 length: 80 // Pixels, or "auto" to fill available space margin: 4 }

spacer

Empty space between elements.

spacer { size: 8 }

Conditionals & Logic

if / else

Show or hide elements based on conditions.

if get(player.health) < 6 { text { value: "⚠ LOW HEALTH" color: #ff0000 fontSize: 14 shadow: true } } else if get(player.health) < 10 { text { value: "Health is getting low" color: #ffaa00 fontSize: 12 } } else { // nothing }

Inline Expressions

Use {} inside string values for inline expressions:

text { value: "You are in the {get(world.dimension)}" } text { value: "XP Level: {get(player.xpLevel)} ({round(get(player.xpProgress) * 100)}%)" }

Math Helpers

Function Description round(x) Round to nearest integer floor(x) Round down ceil(x) Round up abs(x) Absolute value min(x, y) Minimum of two values max(x, y) Maximum of two values clamp(x, min, max) Clamp value between min and max lerp(a, b, t) Linear interpolation pct(value, max) Returns (value / max) * 100 format(x, decimals) Format number to N decimal places

String Helpers

Function Description upper(s) Uppercase string lower(s) Lowercase string concat(a, b) Concatenate two strings trim(s, n) Trim string to N characters replace(s, from, to) Replace substring

Comparison Operators

==, !=, <, >, <=, >=, &&, ||, !

if get(world.dimension) == "nether" && get(player.health) < 10 { text { value: "Danger!" color: #ff0000 } }

Events

React to events to update the widget visually.

on keypress(input.attack) { // Triggered once when key is first pressed text { value: "Attacking!" color: #ff4444 fadeOut: 500 // Fade out after 500ms } } on keyhold(input.sneak) { // Triggered every tick while key is held } on keyrelease(input.jump) { // Triggered once when key is released } on event(player.health < 5) { // Triggered when condition becomes true // Re-triggers if condition goes false then true again } on tick { // Runs every game tick (20 times/sec) } on frame { // Runs every render frame }

Accessibility Toggles

Developers can expose toggles and settings for players with specific accessibility needs. These appear in the widget's right-click settings panel.

settings { toggle "High Contrast Mode" { id: highContrast default: false description: "Increases color contrast for better visibility." } toggle "Reduce Motion" { id: reduceMotion default: false description: "Disables animations and transitions." } toggle "Large Text" { id: largeText default: false description: "Increases all text sizes by 1.5×." } toggle "Colorblind Mode" { id: colorblindMode default: false description: "Replaces color-coded indicators with shapes/patterns." } toggle "Screen Reader Hints" { id: screenReaderHints default: false description: "Shows additional text labels on icon-only elements." } }

Then use the setting values in your widget:

widget { text { value: "HP: {get(player.health)}" color: if setting(highContrast) then #ffff00 else #ffffff fontSize: if setting(largeText) then 18 else 12 shadow: true } }

Player Settings API

Beyond accessibility toggles, you can expose fully custom player-configurable settings.

Setting Types

settings { // Color picker color "Health Bar Color" { id: healthColor default: #ff4444 description: "Color of the health bar fill." } // Slider (numeric range) slider "Widget Opacity" { id: widgetOpacity min: 0.1 max: 1.0 step: 0.05 default: 0.85 description: "Transparency of the widget background." } // Dropdown select select "Display Mode" { id: displayMode options: ["compact", "normal", "expanded"] default: "normal" description: "How much information to show." } // Text input (display label only) text "Custom Label" { id: customLabel default: "My Widget" maxLength: 20 description: "Custom title displayed at the top." } // Toggle (boolean) toggle "Show Widget" { id: showWidget default: true description: "Toggle the entire widget on or off." } // Key binding (input display only — shows which key was pressed) keybind "Highlight Key" { id: highlightKey default: input.forward description: "Which key to highlight in the keystroke display." } }

Using Settings in Widget

widget { opacity: setting(widgetOpacity) visible: setting(showWidget) text { value: setting(customLabel) fontSize: 11 color: #aaaaaa } bar { value: get(player.health) max: get(player.maxHealth) fillColor: setting(healthColor) width: 100 height: 8 } }

Animations

Animations are disabled automatically when the player has reduceMotion enabled (if you defined that toggle). Always check before using animations in accessibility-conscious widgets.

text { value: "⚠ LOW HEALTH" color: #ff0000 animate { property: opacity from: 1.0 to: 0.3 duration: 600 // Milliseconds easing: ease-in-out // "linear", "ease-in", "ease-out", "ease-in-out" loop: true pingpong: true // Reverse on each loop paused: setting(reduceMotion) } }

Transition (on state change)

text { value: "{get(perf.fps)}" color: #ffffff transition { property: color duration: 200 easing: ease-out } }

CPS Implementation Guide

This section is for Hudifine mod developers implementing the input.cps and input.rcps data sources on the Java side.

CPS (clicks per second) is not a value Minecraft tracks natively. You need to implement a click counter using a sliding time window.

How it works

Track every left click (attack) and right click (use) with a timestamp. To get the current CPS, count how many clicks happened in the last 1000ms.

Recommended Java implementation (Fabric + Mixin)

Step 1 - Store click timestamps

// In your HudifineDataProvider or equivalent class private static final List<Long> leftClickTimes = new ArrayList<>(); private static final List<Long> rightClickTimes = new ArrayList<>(); public static void registerLeftClick() { long now = System.currentTimeMillis(); leftClickTimes.add(now); // Prune old entries older than 1 second leftClickTimes.removeIf(t -> now - t > 1000); } public static void registerRightClick() { long now = System.currentTimeMillis(); rightClickTimes.add(now); rightClickTimes.removeIf(t -> now - t > 1000); } public static int getLeftCPS() { long now = System.currentTimeMillis(); leftClickTimes.removeIf(t -> now - t > 1000); return leftClickTimes.size(); } public static int getRightCPS() { long now = System.currentTimeMillis(); rightClickTimes.removeIf(t -> now - t > 1000); return rightClickTimes.size(); }

Step 2 - Hook into mouse clicks via Mixin

@Mixin(MinecraftClient.class) public class MouseClickMixin { @Inject(method = "doAttack", at = @At("HEAD")) private void onLeftClick(CallbackInfo ci) { HudifineDataProvider.registerLeftClick(); } @Inject(method = "doItemUse", at = @At("HEAD")) private void onRightClick(CallbackInfo ci) { HudifineDataProvider.registerRightClick(); } }

Step 3 - Expose to the script engine

Map input.cps → HudifineDataProvider.getLeftCPS() and input.rcps → HudifineDataProvider.getRightCPS() in your data source resolver.

Notes

  • The sliding 1000ms window is the standard used by Lunar Client, Badlion, and most PvP clients
  • doAttack fires on every swing attempt, including misses. This matches how Lunar counts CPS
  • doItemUse fires on right click regardless of what item is held, which also matches expected behavior
  • Both lists are pruned on read, so they stay small even during extended sessions

Full Examples

Minimal FPS Counter

meta { name: "FPS Counter" author: "example" version: "1.0.0" } widget { anchor: top-right offsetX: -10 offsetY: 10 background: #00000066 padding: 6 borderRadius: 4 text { value: "{get(perf.fps)} FPS" color: #ffffff fontSize: 12 shadow: true } }

Keystroke Display

meta { name: "Keystrokes" author: "example" version: "1.0.0" } settings { color "Active Key Color" { id: activeColor default: #ffffff } color "Inactive Key Color" { id: inactiveColor default: #ffffff33 } toggle "Show CPS" { id: showCps default: true } } widget { anchor: bottom-right offsetX: -10 offsetY: -10 direction: column gap: 3 group { direction: row justify: center gap: 3 keyIndicator { key: input.forward label: "W" activeColor: setting(activeColor) inactiveColor: setting(inactiveColor) size: 20 borderRadius: 8 } } group { direction: row gap: 3 keyIndicator { key: input.left label: "A" activeColor: setting(activeColor) inactiveColor: setting(inactiveColor) size: 20 borderRadius: 8 } keyIndicator { key: input.backward label: "S" activeColor: setting(activeColor) inactiveColor: setting(inactiveColor) size: 20 borderRadius: 8 } keyIndicator { key: input.right label: "D" activeColor: setting(activeColor) inactiveColor: setting(inactiveColor) size: 20 borderRadius: 8 } } group { direction: row justify: center keyIndicator { key: input.jump label: "SPACE" activeColor: setting(activeColor) inactiveColor: setting(inactiveColor) size: 20 width: 66 borderRadius: 8 } } group { direction: row gap: 3 keyIndicator { key: input.attack label: "LMB" activeColor: setting(activeColor) inactiveColor: setting(inactiveColor) size: 20 width: 40 borderRadius: 8 } keyIndicator { key: input.use label: "RMB" activeColor: setting(activeColor) inactiveColor: setting(inactiveColor) size: 20 width: 40 borderRadius: 8 } } if setting(showCps) { group { direction: row gap: 6 justify: center text { value: "{get(input.cps)} CPS" color: #aaaaaa fontSize: 10 } text { value: "{get(input.rcps)} CPS" color: #aaaaaa fontSize: 10 } } } }

Full Status HUD

meta { name: "Status HUD" author: "example" version: "1.0.0" description: "Health, hunger, armor, XP and coordinates." } settings { toggle "Show Coordinates" { id: showCoords default: true } toggle "Show Armor" { id: showArmor default: true } toggle "Show XP" { id: showXp default: true } toggle "High Contrast Mode" { id: highContrast default: false description: "Higher contrast colors." } toggle "Large Text" { id: largeText default: false } color "Health Color" { id: healthColor default: #ff4444 } slider "Opacity" { id: opacity min: 0.2 max: 1.0 step: 0.05 default: 0.85 } } widget { anchor: bottom-left offsetX: 10 offsetY: -60 background: #00000066 padding: 8 borderRadius: 6 opacity: setting(opacity) direction: column gap: 5 // Health group { direction: row gap: 6 align: center text { value: "❤" color: setting(healthColor) fontSize: if setting(largeText) then 16 else 11 } bar { value: get(player.health) max: get(player.maxHealth) width: 80 height: if setting(largeText) then 10 else 7 fillColor: setting(healthColor) backgroundColor: #ffffff22 borderRadius: 3 } text { value: "{floor(get(player.health))}/{floor(get(player.maxHealth))}" color: if setting(highContrast) then #ffff00 else #ffffff fontSize: if setting(largeText) then 14 else 10 } } // Hunger group { direction: row gap: 6 align: center text { value: "🍗" color: #ffaa44 fontSize: if setting(largeText) then 16 else 11 } bar { value: get(player.hunger) max: 20 width: 80 height: if setting(largeText) then 10 else 7 fillColor: #ffaa44 backgroundColor: #ffffff22 borderRadius: 3 } text { value: "{get(player.hunger)}/20" color: if setting(highContrast) then #ffff00 else #ffffff fontSize: if setting(largeText) then 14 else 10 } } // XP if setting(showXp) { group { direction: row gap: 6 align: center text { value: "✦" color: #55ff55 fontSize: if setting(largeText) then 16 else 11 } bar { value: get(player.xpProgress) max: 1.0 width: 80 height: if setting(largeText) then 10 else 7 fillColor: #55ff55 backgroundColor: #ffffff22 borderRadius: 3 } text { value: "Lv {get(player.xpLevel)}" color: if setting(highContrast) then #ffff00 else #55ff55 fontSize: if setting(largeText) then 14 else 10 } } } // Armor if setting(showArmor) { separator { direction: horizontal color: #ffffff22 thickness: 1 length: auto margin: 2 } group { direction: row gap: 4 align: center icon { item: get(inventory.helmetItem) size: 14 } icon { item: get(inventory.chestplateItem) size: 14 } icon { item: get(inventory.leggingsItem) size: 14 } icon { item: get(inventory.bootsItem) size: 14 } } } // Coordinates if setting(showCoords) { separator { direction: horizontal color: #ffffff22 thickness: 1 length: auto margin: 2 } text { value: "X {floor(get(world.x))} Y {floor(get(world.y))} Z {floor(get(world.z))}" color: if setting(highContrast) then #ffff00 else #aaaaaa fontSize: if setting(largeText) then 13 else 10 } text { value: "Facing {get(world.facing)} · {get(world.biome)}" color: if setting(highContrast) then #ffff00 else #777777 fontSize: if setting(largeText) then 12 else 9 } } }

This documentation covers HUD Script v1.0. For questions or contributions, share your scripts in the community.

Hudifine - Mod Integration Guide

Hudifine exposes a data provider API that allows any Fabric mod to register custom data sources. Once registered, your data is available inside HUDScript via get(), exactly like built-in sources such as get(perf.fps) or get(player.health).

Requirements

  • Fabric Loader >=0.15.0
  • Minecraft >=26.1.2
  • Hudifine installed as a dependency

1. Add Hudifine as a Dependency

In your build.gradle:

repositories { maven { url = uri("https://cdn.jsdelivr.net/gh/Pacsy1/hudifine@main/public-maven/") } } dependencies { modImplementation "dev.hudifine:hudifine-api:1.0.0" }

In your fabric.mod.json, declare it as a required dependency:

{ "depends": { "hudifine": ">=1.0.0" } }

2. Implement a Data Provider

Hudifine provides typed provider interfaces. Pick the one that matches your data.

Available Provider Types

Interface Return Type Example Use Case IntDataProvider int FPS, ping, entity count FloatDataProvider float TPS, speed, temperature StringDataProvider String Biome name, dimension, status BooleanDataProvider boolean Is sprinting, is on ground

Example: FPS Provider

import dev.hudifine.api.provider.IntDataProvider; public class FpsProvider implements IntDataProvider { @Override public String getId() { return "mymod.fps"; // dot-separated, matches get() call in HUDScript } @Override public String getDisplayName() { return "FPS Counter"; } @Override public int getValue() { return Minecraft.getInstance().getFps(); } }

ID Naming Convention

Provider IDs use dot notation to match how HUDScript data sources are called:

mymod.fps → get(mymod.fps) mymod.ping → get(mymod.ping) mymod.something → get(mymod.something)

IDs must be unique. Duplicate IDs are rejected at load time with a warning in the Hudifine log.

3. Register the Provider via Entrypoint

In your fabric.mod.json, register your provider class under the hudifine:provider entrypoint:

{ "entrypoints": { "hudifine:provider": [ "com.yourmod.FpsProvider" ] } }

Multiple providers at once:

{ "entrypoints": { "hudifine:provider": [ "com.yourmod.FpsProvider", "com.yourmod.PingProvider", "com.yourmod.BiomeProvider" ] } }

Hudifine discovers and loads all registered providers automatically at startup. No additional initialization code is required on your end.

4. Using Your Data in HUDScript

Once registered, your provider is accessible in any HUDScript using get(), identical to built-in sources.

Basic Usage

widget { anchor: top-right offsetX: -10 offsetY: 10 background: #00000066 padding: 6 borderRadius: 4 text { value: "{get(mymod.fps)} FPS" color: #ffffff fontSize: 12 shadow: true } }

With Conditionals

widget { anchor: top-right offsetX: -10 offsetY: 10 background: #00000066 padding: 6 borderRadius: 4 text { value: "{get(mymod.fps)} FPS" color: if get(mymod.fps) < 30 then #ff4444 else if get(mymod.fps) < 60 then #ffaa00 else #ffffff fontSize: 12 shadow: true } }

With a Bar

widget { anchor: bottom-left offsetX: 10 offsetY: -10 background: #00000066 padding: 8 borderRadius: 6 direction: column gap: 4 text { value: "My Mod Stats" color: #aaaaaa fontSize: 10 } group { direction: row gap: 6 align: center text { value: "FPS" color: #aaaaaa fontSize: 10 } bar { value: get(mymod.fps) max: 260 width: 80 height: 7 fillColor: #55ff55 backgroundColor: #ffffff22 borderRadius: 3 } text { value: "{get(mymod.fps)}" color: #ffffff fontSize: 10 } } }

With Player Settings

Your provider's data works with all HUDScript features including the Player Settings API:

settings { toggle "Show My Mod Stats" { id: showStats default: true } color "Stat Color" { id: statColor default: #55ff55 } } widget { visible: setting(showStats) text { value: "{get(mymod.fps)} FPS" color: setting(statColor) fontSize: 12 shadow: true } }

5. Optional: Provider Metadata

Implement HudifineProviderMeta to show richer information about your source in Hudifine's in-game provider browser:

import dev.hudifine.api.provider.IntDataProvider; import dev.hudifine.api.provider.HudifineProviderMeta; public class FpsProvider implements IntDataProvider, HudifineProviderMeta { @Override public String getId() { return "mymod.fps"; } @Override public String getDisplayName() { return "FPS Counter"; } @Override public int getValue() { return Minecraft.getInstance().getFps(); } // Optional metadata @Override public String getDescription() { return "Current rendered frames per second."; } @Override public String getCategory() { return "performance"; // Groups it with perf.fps, perf.tps etc. in the browser } @Override public String getUnit() { return "fps"; } }

6. Performance Guidelines

getValue() is called every frame. Keep it fast:

  • Cache expensive calculations, and update them on a tick, not per frame
  • Avoid object allocation inside getValue(), use primitives wherever possible
  • If your data only changes on certain events, update a field on that event and return the cached field

Good pattern:

public class EntityCountProvider implements IntDataProvider { private int cachedCount = 0; @SubscribeEvent public void onTick(TickEvent event) { // Runs 20x/sec instead of every frame this.cachedCount = world.getEntities().size(); } @Override public String getId() { return "mymod.entityCount"; } @Override public String getDisplayName() { return "Nearby Entity Count"; } @Override public int getValue() { return cachedCount; // Just a field read — instant } }

7. Marketplace Publishing

You should probably title your mod with the Hudifine title, to do this, simply start your mod's name with 'Hudifine' for better discoverability.

8. Building HUDs with Your Data

This guide only covers the mod integration side. To learn how to actually write HUDScript widgets that use your provider's data, elements, layout, styling, conditionals, animations, settings, and more, refer to the HUDScript Developer Documentation above.

Summary

Step What you do 1 Add Hudifine API as a dependency 2 Implement a typed provider interface with a dot-notation ID 3 Register it in fabric.mod.json under hudifine:provider 4 Users access your data via get(yourid) in HUDScript 5 (Optional) Implement HudifineProviderMeta for the in-game browser 6 Keep getValue() fast, it runs every frame 7 (Optional) Publish your mod on Modrinth with Hudifine included in its name 8 See the HUDScript Developer Documentation to build actual widgets

Hudifine Mod Integration Guide - v1.0

Compatibility

Mod Loaders

Fabric

Game Versions

26.1.2

Screenshots

Similar Mods

External Resources