promotional bannermobile promotional banner

Ember's Text API

Text rendering and styling API with effects, animations, and custom font support.

File Details

Ember's Text API 3.0.0-beta.2 (Forge 1.20.1)

  • B
  • Jun 12, 2026
  • 4.75 MB
  • 277
  • 1.20.1
  • Forge

File Name

emberstextapi-forge-1.20.1-3.0.0-beta.2-all.jar

Supported Versions

  • 1.20.1

Curse Maven Snippet

Forge

implementation fg.deobf("curse.maven:embers-text-api-1345948:8235147")
Curse Maven does not yet support mods that have disabled 3rd party sharing

Learn more about Curse Maven

Changelog

v3.0.0-beta.2 — 2026-06-11

Neon glow

  • New glow effect (alias neon) renders an SDF-based halo around glyphs: <glow>, <glow radius=4 intensity=1.5 color=#00ffcc>, <glow softness=0 core=0.6> for a sharp neon-tube look, freq for pulsing. When no color is given the halo picks up each glyph's final color, so <glow><rainbow> animates the halo with the text.
  • Glow color resolves per glyph at emit time. Replaces NeonEffect — the neon markup name still works and now maps to the new effect.
  • Per-version render paths: core shaders on 1.21.1, the new render-pipeline API on 26.1, and a bitmap-only runtime SDF path on 1.20.1 (no FreeType required at runtime).
  • Glow distance fields bake off the render thread with a per-frame budget; the atlas uploads only changed slots. Drop shadows are suppressed on glowing glyphs so halos don't double-darken.

MSDF font pipeline

  • Pre-baked MSDF glyphs now persist to disk on all versions — fonts skip the bake cost on every launch after the first.
  • SDF providers attach once per resource reload; fixed the glyph baseline on 1.20.1.
  • Forge 1.20.1 embeds the FreeType natives in the -all jar and fails the binding probe cleanly on servers without them.

KubeJS integration (NeoForge 1.21.1)

  • New EmbersText binding for scripts: send/queue immersive messages, manage HUD elements, and style item names and tooltips.
  • New EmbersTextEvents.itemTooltip client event for per-stack tooltip styling from scripts.

Version parity

  • Item-name styling and the EmbersTextFacade entry point now ship on every loader/version target, and the item-name mixin is correctly registered on NeoForge 26.1.
  • FTB Quests view tracking ported to 26.1.
  • Emojiful compat (1.20.1/1.21.1): effect shadows are dimmed and glow emits in the Emojiful render path. Not yet available on 26.1.
  • Obfuscate reveal/hide order is now computed relative to the span start on 1.20.1/1.21.1, matching 26.1 — spans that start mid-text reveal correctly.
  • Immersive overlay messages now attach span effect data (effect list, typewriter track, obfuscate keys) to their component styles, so typewriter, obfuscate reveal/random, scroll, and shadow animate in overlays the same way they do in chat. The per-char render tier is gated against re-applying style-carried effects (26.1 covers this with its existing render bypass).

Performance

  • Eliminated multiple per-frame allocation and draw-call hot spots in the message renderer. Stress-test FPS at 50 concurrent messages on NeoForge 26.1 improved from ~70 to ~1500+ against a ~2000 FPS baseline.
  • EffectApplicator.applyEffects no longer recursively applies sibling-spawning effects onto siblings they just created — those level-2 grandchildren never render. New Effect#spawnsSiblings() opt-out lets sibling-spawning effects skip the redundant pass.
  • ImmersiveMessage.buildComponentFromSpans caches its output keyed on the typewriter index snapshot.
  • Per-codepoint glyph width and FormattedCharSequence now cached per (font, codepoint, bold) on each message.
  • Effects can opt out of per-char dispatch via Effect#requiresPerChar(). TypewriterEffect opts out when cursor=false — its cropping is already handled at the component layer.
  • GuiGraphics.drawManaged wraps message rendering on 1.21.1/1.20.1 to collapse per-text-call buffer flushes (not applicable on 26.1's queued render model).
  • Background border drawing replaced per-pixel fill loops with single rotated fillGradient calls per strip. Top/bottom borders use a 90° pose rotation so vanilla's vertical gradient becomes a smooth GPU-interpolated horizontal one.
  • fillHorizontalGradient (multi-stop background fill) uses the same rotation trick — N-stop gradient renders as N-1 rotated calls per strip instead of one fill per pixel.

Background gradient extensions

  • [bg gradient=a,b,c,...] markup now accepts any number of stops (previously capped at 2).
  • [bg border=a,b,c,...] markup adds N-stop border gradient support. Side borders use the first and last stops so corners blend cleanly with the N-stop top/bottom edges.
  • New ImmersiveMessage.borderGradient(ImmersiveColor... stops) varargs API. The existing 2-arg overload continues to work unchanged.

Effect system unification

  • Fade is now a MessageEffect under the hood. fadeInTicks / fadeOutTicks builders construct and add FadeInMessageEffect / FadeOutMessageEffect to the message's effect list. The parallel field-based fade state on ImmersiveMessage is gone.
  • New effect names available in markup and MessageEffectRegistry: fadein, fadeout.
  • MessageEffect#target() (default EffectTarget.TEXT) controls whether the effect's pose transform scopes to text, background, or both.
  • New AlphaContributor sub-interface lets effects contribute to a per-frame alpha multiplier shared by text and background. FadeInMessageEffect and FadeOutMessageEffect implement it.

Slide effect background option

  • <slide> now accepts a target param (text, background, all). Default is all, which slides text and background together. The previous beta.1 behavior of sliding text only matches target=text.

Metadata

  • Loader metadata now declares the EML licence (was incorrectly GPL-3.0) and the licence text is embedded in every jar.

Breaking changes from 3.0.0-beta.1

  • ImmersiveMessage constructor, builder, fromMarkup, fromSpans, getDuration, and setDuration now take/return int ticks instead of float. The internal field remains float for partial-tick interpolation.
  • ImmersiveMessage.typewriter(float, boolean), .typewriter(float), .typewriterCentered(float), and .obfuscate(ObfuscateMode, float) are removed. Use <type> / <obfuscate> markup, or set typewriter/obfuscate at the span level via TextSpan.typewriter(float) / .typewriterCentered(float) / .obfuscate(...).
  • TextSpan.typewriter(float speed, boolean center) is removed. Use TextSpan.typewriter(float speed) or TextSpan.typewriterCentered(float speed).
  • ImmersiveMessage no longer exposes public fadeInTicks / fadeOutTicks / typewriter* / obfuscate* fields. fadeInTicks / fadeOutTicks builders still work and now construct effects; reflection-based consumers must read from getMessageEffects().
  • NBT key "Duration" is now stored as IntTag (previously FloatTag). NBT saved by 3.0.0-beta.1 will not load.
  • NBT: legacy fadeIn, fadeOut, Typewriter, TypewriterSpeed, TypewriterCenter, ObfuscateMode, ObfuscateSpeed keys are no longer read or written. Fade state lives in the existing MessageEffects list (e.g. "fadein t=20"); typewriter and obfuscate live on TextSpan or come from markup.
  • S2C message wire format encodes duration as VarInt (previously Float), the legacy fade/typewriter/obfuscate per-feature fields are gone from the payload, and the payload gained a trailing (boolean, VarInt, int[]) block for borderGradientStops. The effects list is the sole carrier for fade. 3.0.0-beta.1 clients/servers are incompatible.
  • NBT gained an optional BorderGradient list. Saves without the key load unchanged.
  • Default <slide> behavior changed from text-only to text+background. Pass target=text in markup, or construct SlideMessageEffect with that param, to restore the old shape.