Popups are temporary HUD elements that appear in response to events and automatically disappear after a duration. They’re perfect for damage indicators, notifications, quest updates, and any information that should briefly grab the player’s attention.
A Popup is a HudObject that:
Appears temporarily when triggered by an event
Automatically disappears after a configured duration
Can be stacked (multiple popups showing simultaneously)
Supports animations and movement effects
Can be queued when too many appear at once
Unlike Huds , Popups are event-driven and temporary. They show up, animate, then fade away.
Basic Structure
Popups are defined in YAML files in the popups/ directory:
my_popup_name :
triggers : # What causes this popup to show
1 :
class : entity_attack
layouts : # What to display
1 :
name : damage_layout
gui :
x : 0
y : 0
pixel :
x : 128
y : 32
duration : 20 # How long to display (ticks)
Here’s the actual entity health popup from BetterHud’s source:
entity_health_popup :
move :
duration : 3
pixel :
x-equation : 0
y-equation : 40t # Move up 40 pixels per second
push : true # Stack multiple popups
key-mapping : true # Allow per-entity tracking
triggers :
1 :
class : entity_attack # Show when attacking entity
layouts :
1 :
name : entity_health_image
gui :
x : 0
y : 0
pixel :
x : 128 # Center of screen
y : 32 # Near top
2 :
name : entity_health_text
gui :
x : 0
y : 0
pixel :
x : 128
y : 32
duration : 20 # Show for 1 second (20 ticks)
This popup:
Triggers on attack
When a player attacks an entity, the entity_attack trigger fires
Shows health info
Displays the entity’s health using both image and text layouts
Moves upward
Animates upward at 40 pixels per second using y-equation: 40t
Stacks multiple hits
With push: true, hitting the same entity multiple times shows multiple popups
Disappears
After 20 ticks (1 second), the popup fades away
The referenced layouts are defined in layouts/entity-layout.yml:
entity_health_image :
images :
1 :
name : player # Show player head/icon
y : -20
2 :
name : entity_health_empty # Background bar
3 :
name : entity_health_white # Bar overlay
4 :
name : entity_health_red # Damage indicator
entity_health_text :
texts :
1 :
name : entity_font
pattern : "Health: <#FFD800>[entity_health] // [entity_max_health]"
placeholder-string-format :
number : "#,###" # Format numbers with commas
y : -16
x : -32
scale : 0.5
align : left
Popups can combine images and texts in their layouts to create rich notifications.
Triggers
Triggers determine when a popup appears:
my_popup :
triggers :
1 :
class : entity_attack # Built-in trigger class
cooldown : 5 # Minimum ticks between triggers
Common Trigger Classes
Trigger When it Fires Use Case entity_attackPlayer attacks entity Damage indicators player_deathPlayer dies Death messages player_joinPlayer joins server Welcome messages player_quitPlayer leaves server Goodbye messages commandCommand executed Command feedback customAPI call Plugin integration
Use the API to trigger popups programmatically: popup . show ( UpdateEvent . of (player), hudPlayer);
Duration and Timing
Control how long popups display:
my_popup :
duration : 40 # Display for 40 ticks (2 seconds)
frame-type : local # Use popup-local timing (default)
Frame Types
Local Timing frame-type: local - Each popup has its own animation timeline starting from 0
Global Timing frame-type: global - All popups share the global tick counter
Local timing is recommended for most popups as it ensures animations play consistently regardless of when the popup was triggered.
Movement and Animation
Popups can move and animate during their lifetime:
my_popup :
move :
duration : 3 # Movement speed (higher = slower)
pixel :
x-equation : 10t # Move right 10 pixels per second
y-equation : -30t # Move up 30 pixels per second
layouts :
# ...
Animation Variables
t - Time since popup appeared (0 to duration)
Mathematical functions - sin, cos, exp, log, etc.
Movement Examples
Float Up
Bounce
Fade In
Spiral
move :
pixel :
x-equation : 0
y-equation : -20t # Negative = upward
move :
pixel :
x-equation : 0
y-equation : 30sin(t * pi)
move :
duration : 10
pixel :
x-equation : 5 - t/2 # Move less as time goes on
y-equation : 0
move :
pixel :
x-equation : 10cos(t)
y-equation : 10sin(t)
Control how multiple popups interact:
my_popup :
max-stack : 5 # Allow up to 5 popups simultaneously
push : true # Stack new popups (don't replace old ones)
group : damage_group # Group name for organization
Push Behavior
Push Enabled push: true - New popups stack above old ones. Old popups remain visible.
Push Disabled push: false - New popups replace old ones in the same group.
Stack Sorting
Control the order of stacked popups:
my_popup :
push : true
sort-type : recent # Most recent on top
# Options: recent, old
When max-stack is reached, the oldest popup is removed to make room for the new one.
Organize related popups into groups:
damage_popup :
group : combat_notifications
max-stack : 3
# ...
heal_popup :
group : combat_notifications
max-stack : 3
# ...
Popups in the same group:
Share the same stack limit
Can be hidden/shown together
Are positioned relative to each other
Use groups to prevent too many notifications from cluttering the screen. All notifications in a group share the stack limit.
Key Mapping
Track popups per-entity or per-object:
entity_health_popup :
key-mapping : true # Each entity gets its own popup
push : true
triggers :
1 :
class : entity_attack
With key-mapping: true:
Attacking Entity A and Entity B shows two separate popups
Each entity’s popup updates independently
Perfect for per-target damage indicators
The “key” is typically the entity UUID or a unique identifier from the trigger event.
Trigger popups programmatically:
import kr.toxicity.hud.api.BetterHud;
import kr.toxicity.hud.api.popup.Popup;
import kr.toxicity.hud.api.update.UpdateEvent;
// Get popup by name
Popup popup = BetterHud . getInstance ()
. getPopupManager ()
. getPopup ( "my_popup" );
// Create update event with custom variables
Map < String , String > variables = new HashMap <>();
variables . put ( "damage" , "15" );
variables . put ( "killer" , "Zombie" );
UpdateEvent event = UpdateEvent . of (player)
. withVariables (variables);
// Show popup
if (popup != null ) {
popup . show (event, hudPlayer);
}
With Skript
command / damage:
trigger:
set { _vars ::damage} to 42
set { _vars ::type} to "critical"
show popup "damage_popup" to player with variable of { _vars :: * }
Variables passed in the UpdateEvent can be used as placeholders in the popup layout.
Remove popups programmatically:
// Hide a specific popup
popup . hide (hudPlayer);
// Hide all popups in a group
String groupName = popup . getGroupName ();
var group = hudPlayer . getPopupGroupIteratorMap (). remove (groupName);
if (group != null ) {
group . clear ();
}
Advanced: Conditions
Show popups only when conditions are met:
my_popup :
conditions :
- "<player_health> < 5" # Only show when low health
- "<player_world> = 'world'" # Only in specific world
triggers :
# ...
Complete Example
A full-featured damage indicator popup:
damage_indicator :
# Trigger
triggers :
1 :
class : entity_attack
cooldown : 2
# Stacking
max-stack : 8
push : true
sort-type : recent
key-mapping : true
group : combat_indicators
# Timing
duration : 25
frame-type : local
# Movement
move :
duration : 2
pixel :
x-equation : 5cos(t * 2)
y-equation : -40t
# Display
layouts :
1 :
name : damage_text
gui :
x : 50
y : 40
pixel :
x : 0
y : 0
With the layout:
damage_text :
texts :
1 :
name : damage_font
pattern : "<red>-[damage]"
scale : 1.5
align : center
animations :
duration : 25
x-equation : 0
y-equation : 0
Best Practices
Don’t overwhelm players with too many popups
Use movement to direct attention (up = good, down = bad)
Keep popup text concise
Use groups to organize related notifications
Test animations at different frame rates
Ensure popups don’t obscure important UI elements
Use consistent styling across popup types
Next Steps
Triggers Learn about all available trigger types
Layouts Configure text and image layouts
API: Popups Trigger popups programmatically
Huds Learn about persistent HUD displays