Compatibility
Supported Loaders and Versions
Section titled “Supported Loaders and Versions”| Minecraft | Loaders | Java | Status |
|---|---|---|---|
| 1.20.1 | Forge 47.4.0, Fabric | 17+ | Fully supported |
| 1.21.1 | NeoForge, Fabric | 21+ | Fully supported |
The API surface is identical on all loaders. Differences are limited to the mod entry point and network registration.
HUD / Chat Layering
Section titled “HUD / Chat Layering”Immersive messages are rendered in a HUD layer intentionally ordered after vanilla chat. This prevents chat background and chat glyphs from hiding immersive overlays when they overlap.
| Loader | Render Hook | Chat Ordering |
|---|---|---|
| Forge 1.20.1 | RegisterGuiOverlaysEvent | registerAbove(VanillaGuiOverlay.CHAT_PANEL, ...) |
| NeoForge 1.21.1 | RegisterGuiLayersEvent | registerAbove(VanillaGuiLayers.CHAT, ...) |
| Fabric 1.20.1 / 1.21.1 | HudRenderCallback | Callback runs after InGameHud chat rendering |
The render path also forces standard HUD state (blend on, depth test off, color reset) and uses a positive GUI Z translate to keep immersive content visually on top without changing layout logic.
Font Renderers
Section titled “Font Renderers”ETA hooks into Minecraft’s BakedGlyph rendering at the character level. This means it works with:
- The default Minecraft font renderer
- Custom fonts loaded via resource packs (using the
<font>tag) - ETA’s built-in SDF font provider (
emberstextapi:sdf) for TrueType/OpenType fonts - Any font that produces
BakedGlyphobjects through the standard pipeline
It does not work with external text rendering libraries that bypass Minecraft’s glyph system entirely.
SDF Font Provider
Section titled “SDF Font Provider”ETA includes a custom GlyphProvider type (emberstextapi:sdf) that renders vector fonts using signed distance field textures. SDF glyphs are baked into MC’s standard FontTexture atlas via SheetGlyphInfo, and a mixin on FontTexture swaps the GlyphRenderTypes to use a custom SDF fragment shader. Because BakedGlyph carries its own GlyphRenderTypes, all existing effects work on SDF glyphs with zero changes to the effects pipeline.
The SDF provider requires LWJGL FreeType. MC 1.21.1 includes FreeType natively. For MC 1.20.1 (both Forge and Fabric), ETA includes a NativeFreeType compatibility layer that calls FreeType functions directly via JNI.invoke*() by address, bypassing the LWJGL 3.3.3 FreeType wrapper class that is incompatible with MC 1.20.1’s LWJGL 3.3.1. SDF fonts work on all supported platforms.
MC 1.20.1 Alpha-Preserving Blend (Fringe Fix)
Section titled “MC 1.20.1 Alpha-Preserving Blend (Fringe Fix)”MC 1.20.1’s blit-to-screen reads the framebuffer alpha channel. Because MSDF text produces semi-transparent edge fragments for anti-aliasing, those alpha values caused a visible halo (fringe) around SDF-rendered text on all 1.20.1 loaders. MC 1.21.1 does not exhibit this issue because its blit pipeline ignores framebuffer alpha.
The fix uses an alpha-preserving blend function defined in the SDF shader JSON files (rendertype_eta_sdf_text.json and rendertype_eta_sdf_text_see_through.json). The blend section specifies srcalpha=0, dstalpha=1, which tells OpenGL to leave the framebuffer alpha untouched while blending RGB normally. This is defined in the shader JSON rather than in a TransparencyStateShard because MC 1.20.1’s ShaderInstance.apply() applies the JSON blend immediately before the draw call — ensuring nothing can override it.
The immersive renderer normalizes very low alpha bytes (0..3) to 0 before glyph draw. This avoids a vanilla font edge case where near-zero alpha can appear as fully opaque for a single frame during fade-in/fade-out.
Other Text Mods
Section titled “Other Text Mods”If another mod also hooks into BakedGlyph or StringRenderer, there may be conflicts depending on mixin priority. ETA’s mixin only activates its custom path when an EffectSettings object is present — otherwise it falls through to vanilla rendering. This means:
- Text not using ETA effects renders completely normally.
- Conflicts are most likely when two mods both try to modify the same glyph rendering call.
If you encounter a conflict with another mod, report it with the mod combination and mixin configuration. Priority adjustments in the mixin config can usually resolve ordering conflicts.
Emojiful Compatibility
Section titled “Emojiful Compatibility”ETA automatically detects Emojiful and enables a compatibility layer. No configuration is needed — effects work alongside emoji sprites transparently.
How It Works
Section titled “How It Works”Emojiful replaces Minecraft’s font renderer with its own EmojiCharacterRenderer, which bypasses ETA’s normal rendering hooks. ETA includes an optional @Pseudo mixin that intercepts Emojiful’s character renderer and applies effects to non-emoji characters, while letting Emojiful handle emoji sprite rendering normally.
- Regular characters in styled text get full ETA effects (rainbow, wave, shake, etc.)
- Emoji sprites render normally through Emojiful’s pipeline
- Mixed text like
<rainbow>Hello :thinking:</rainbow>— “Hello” gets the rainbow effect, the emoji renders as its sprite
Supported Versions
Section titled “Supported Versions”| Minecraft | Loader | Emojiful Version |
|---|---|---|
| 1.20.1 | Forge | 4.x (EmojiFontRenderer) |
| 1.21.1 | NeoForge | 5.x (EmojiFontHelper) |
| 1.20.1 | Fabric | 4.x (if available) |
| 1.21.1 | Fabric | 5.x (if available) |
Graceful Degradation
Section titled “Graceful Degradation”Emojiful is not a dependency. The compatibility mixin uses @Pseudo (target class may not exist) and require = 0 (injection is optional). If Emojiful is not installed, the mixin is silently skipped. If a future Emojiful update changes its internals, the mixin simply won’t apply — ETA continues to work normally, and effects on emoji-containing text fall back to Emojiful’s default rendering.
FTB Quests Integration
Section titled “FTB Quests Integration”ETA includes an optional mixin for FTB Quests (QuestScreenMixin) that enables immersive messages to render in quest UI screens. This mixin is included in the mixin config but gracefully fails if FTB Quests is not present — no errors are thrown.
Performance Considerations
Section titled “Performance Considerations”Per-Character Cost
Section titled “Per-Character Cost”Effects are applied per character per frame:
Cost ≈ (character count) × (effects per character) × (effect complexity)Most simple effects (wave, shake, rainbow) are very cheap — a few math operations per character. Complex effects (neon, glitch) are more expensive because they create sibling layers.
Sibling Layer Cost
Section titled “Sibling Layer Cost”| Effect | Extra Render Passes Per Character |
|---|---|
| Neon (q=1) | ~6 |
| Neon (q=2, default) | ~12 |
| Neon (q=3) | ~20 |
| Glitch (slices=2) | 1 slice layer (+ optional chromatic) |
| Glitch (slices=4) | 3 slice layers |
Recommendation: Use q=1 for neon on messages with many characters. Reserve q=3 for short hero text (titles, single words). Don’t stack neon on top of glitch on 50-character strings.
TextLayoutCache
Section titled “TextLayoutCache”TextLayoutCache caches computed text layouts and is cleared automatically when the GUI scale changes. It significantly reduces redundant layout calculations for messages that don’t change between frames.
Integration Tips
Section titled “Integration Tips”Always Send from the Server Thread
Section titled “Always Send from the Server Thread”EmbersTextAPI.sendMessage(serverPlayer, immersiveMessage);Never construct or render ImmersiveMessage objects directly on the client side. Rendering is always client-only; sending is always server-only.
Thread Safety
Section titled “Thread Safety”EffectRegistryis thread-safe for reads after initialization.- Effect registration must happen during mod initialization events, not at arbitrary runtime.
ClientMessageManageroperations happen on the main client thread.
Message Lifecycle
Section titled “Message Lifecycle”- Messages have a finite duration (in ticks). Plan your durations accordingly.
- Use
fadeIn/fadeOutto avoid jarring appearance/disappearance. - For persistent UI elements: use
sendUpdateMessage()to refresh a message before it expires, rather than closing and reopening it.
Avoiding Common Pitfalls
Section titled “Avoiding Common Pitfalls”- Don’t stack many neon effects. At
q=2, neon on a 50-character message generates 600 extra render passes per frame. - Don’t send a message every tick. Use
sendUpdateMessage()with the same UUID to update content without creating new network traffic. - Don’t register effects too early. Register during
FMLClientSetupEvent(Forge/NeoForge) oronInitializeClient()(Fabric), not earlier. - Always use
ValidationHelper.clamp()for numeric parameters in custom effects to prevent extreme values. - Don’t depend on specific message UUIDs across server restarts — UUIDs are generated fresh each time
sendMessage()is called.