• v3.0.0-beta.2 39b1b1a9b1

    Ember's Text API 3.0.0-beta.2
    Some checks failed
    Publish / Build Fabric 1.20.1 (push) Has been cancelled
    Publish / Build Fabric 1.21.1 (push) Has been cancelled
    Publish / Build Fabric 26.1 (push) Has been cancelled
    Publish / Build NeoForge 1.21.1 (push) Has been cancelled
    Publish / Build NeoForge 26.1 (push) Has been cancelled
    Publish / Build Forge 1.20.1 (push) Has been cancelled
    Publish / Publish Fabric 1.20.1 (push) Has been cancelled
    Publish / Publish Fabric 1.21.1 (push) Has been cancelled
    Publish / Publish Fabric 26.1 (push) Has been cancelled
    Publish / Publish Forge 1.20.1 (push) Has been cancelled
    Publish / Publish NeoForge 1.21.1 (push) Has been cancelled
    Publish / Publish NeoForge 26.1 (push) Has been cancelled
    Publish / Publish to Maven (Forgejo registry) (push) Has been cancelled
    Publish / Create Forgejo release (push) Has been cancelled
    Publish / Announce release in Discord (push) Has been cancelled
    Pre-release

    TysonTheEmber released this 2026-06-11 22:08:41 -06:00 | 50 commits to master since this release

    Downloads

    Pick the jar that matches your loader and Minecraft version. The same files are also on:

    Changelog

    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.
    Downloads