EnderFrameLib
EnderFrameLib is a comprehensive, lightweight HTML/CSS rendering and UI framework for Minecraft (NeoForge 1.21.1). It brings modern web layout engines, reactive data bindings, animations, media playback, and 3D/2D interactive surfaces directly into Minecraft without requiring an embedded Chromium browser (CEF).
✨ Key Features
- Pure Java HTML & CSS Engine: Supports Block, Flexbox (
flex-direction,flex-wrap,gap,flex-grow,flex-shrink), and CSS Grid (grid-template-columns,repeat(),fr,span) layouts. - Virtual DOM & Diff Engine: Efficiently computes differences between virtual and real DOM trees (
DomDiffEngine) and supports lightweight delta-patch packets (dom_patch) for high-frequency updates. - CSS Transitions & Keyframe Animations: Hardware-accelerated interpolation for
transform(translate,scale,rotate),opacity,color, andbackground-color, plus@keyframesanimations and:hover,:active, and:focuspseudo-classes. - Multi-Surface Rendering:
- 3D World Multiblock Screens: Automatic raytrace hit-testing (click, drag, scroll, hover) and Level-of-Detail (LOD) rendering.
- 3D Handheld Items: Render live interactive web UIs directly onto items in first-person or third-person views.
- 2D GUI Screens: Ready-to-use
AbstractWebScreenbase class with pan, zoom, and keyboard input support. - AR HUD & 3D Entity Trackers: Declarative HUD overlays and floating 3D entity status bars driven by HTML templates and server-synced NBT data.
- Minecraft-Native Media & Assets: Render Minecraft items (
item:), potion effects (effect:), player face avatars (player:), remote web images (https://), and video/audio streams (including YouTube viayt-dlpand 3D spatial OpenAL speaker linking). - Declarative Pub/Sub Data Binding: Bind server-side data channels and virtualized scrolling lists (
data-bind,data-list) directly in HTML without writing boilerplate sync code.
🚀 Getting Started
1. Network & Callback Initialization
Register EnderFrameNetwork on your mod event bus and initialize the common callbacks during setup:
public MyMod(IEventBus modEventBus) {
modEventBus.addListener(EnderFrameNetwork::registerPayloads);
}
private void clientSetup(final FMLClientSetupEvent event) {
event.enqueueWork(() -> {
EnderFrameNetwork.initCommonCallbacks();
// Optional: Hook client-side UI events (onclick, oninput, onchange, etc.)
ClientScreenInteractionManager.clientEventCallback = (sessionId, elementId, eventName, actionName, value) -> {
// Handle event in your custom scripting engine or client handler
};
});
}
2. Creating a 3D World Screen Block
Implement IWebScreenBlockEntity on your BlockEntity and register WebBlockRenderer:
public class MyScreenBlockEntity extends BlockEntity implements IWebScreenBlockEntity {
@Override public boolean isMaster() { return true; }
@Override public BlockPos getScreenMasterPos() { return this.worldPosition; }
@Override public Direction getFacing() { return getBlockState().getValue(BlockStateProperties.HORIZONTAL_FACING); }
@Override public int getScreenWidth() { return 3; } // Width in blocks
@Override public int getScreenHeight() { return 2; } // Height in blocks
@Override public String getSessionId() { return "my_screen_session"; }
}
Register the renderer in EntityRenderersEvent.RegisterRenderers:
event.registerBlockEntityRenderer(MyMod.MY_SCREEN_BE.get(), WebBlockRenderer::new);
Raytrace clicking, dragging, hovering, scrolling, and <input> text editing are handled automatically by ClientScreenInteractionManager.
3. Rendering on a 3D Handheld Item
Use WebItemRendererHelper inside your BlockEntityWithoutLevelRenderer:
@Override
public void renderByItem(ItemStack stack, ItemDisplayContext ctx, PoseStack pose, MultiBufferSource buffer, int light, int overlay) {
pose.pushPose();
WebItemRendererHelper.applyFirstPersonHandShift(stack, ctx, pose);
// Draw your item model here...
WebItemRendererHelper.renderWebOnItem(
stack, ctx, pose, buffer, light, overlay,
768, 1024, // Virtual resolution (px)
0.499f, 0.5f, 5f / 16f, // Local origin (X, Y, Z)
-90f, 0.375f // Y-rotation and physical width in blocks
);
pose.popPose();
}
🖥️ Server-Side Session & DOM Management
Standalone Sessions
You can create and control a WebSession from the server at any time:
int widthPx = 3 * (int) WebRenderCore.RESOLUTION;
int heightPx = 2 * (int) WebRenderCore.RESOLUTION;
WebSession session = WebSessionManager.getOrCreateStandaloneSession("my_screen_session", widthPx, heightPx);
// Load full HTML & CSS
session.loadWithCss("<div id='counter'>0</div>", "#counter { font-size: 2rem; color: #00e5ff; }");
// Send lightweight delta patches without re-sending the entire DOM tree
session.patchText("counter", "42");
session.patchStyle("counter", "color", "#55ff55");
session.patchClass("counter", "highlighted", "add");
session.patchInnerHtml("container", "<p>Updated dynamically!</p>");
Programmatic DOM Manipulation (HtmlNode & DomUtils)
Manipulate nodes directly on either the server or client:
HtmlNode node = doc.root.querySelector("#main-panel .item");
node.addClass("active");
node.toggleClass("hidden");
node.setStyleProperty("background-color", "rgba(0, 122, 204, 0.8)");
node.setTextContent("Hello Minecraft!");
node.setInnerHTML("<span>Nested <b>HTML</b></span>");
🎨 Supported HTML & CSS Reference
HTML Tags
- Structure & Layout:
<div>,<section>,<main>,<header>,<footer>,<article>,<nav>,<span>,<p>,<h1>,<h2>,<h3>,<ul>,<li>,<hr>,<br>,<template> - Interactive & Forms:
<button data-sound="minecraft:ui.button.click" onclick="myAction"><input type="text" placeholder="Enter text..." oninput="onType" onchange="onSubmit" /><input type="checkbox" checked />/<input type="radio" /><input type="range" min="0" max="100" value="50" /><select onchange="onSelect"><option value="a">Option A</option><option value="b">Option B</option></select><progress value="75" max="100"></progress>
- Media:
<img src="..." />,<video src="..." />
Special Image Sources (<img>)
| Prefix | Example | Description |
|---|---|---|
item: |
<img src="item:minecraft:diamond_sword" /> |
Renders a 3D/2D Minecraft ItemStack. |
effect: |
<img src="effect:minecraft:speed" /> |
Renders a MobEffect icon from the texture atlas. |
player: |
<img src="player:@self" /> or player:Notch |
Renders a player's 2-layer skin face (base + hat layer). |
https:// |
<img src="" rel="noopener nofollow" target="_blank">https://example.com/icon.png" /> |
Asynchronously downloads and caches remote images (supports Google Drive direct links). |
ResourceLocation |
<img src="minecraft:textures/block/stone.png" /> |
Renders any registered Minecraft texture. |
Supported CSS Properties
- Layout Mode:
display: block | inline | inline-block | flex | grid | none - Flexbox:
flex-direction: row | column,flex-wrap: nowrap | wrap,justify-content: flex-start | center | flex-end | space-between,align-items: stretch | flex-start | center | flex-end,flex,flex-grow,flex-shrink,flex-basis,gap - CSS Grid:
grid-template-columns,grid-template-rows(supportspx,%,fr,repeat()),grid-column,grid-row(span N,start / end),gap - Positioning & Box Model:
position: static | relative | absolute | fixed,top,right,bottom,left,z-index,width,height,min-width,max-width,min-height,aspect-ratio,margin,padding,box-sizing: border-box | content-box - Units & Functions:
px,%,vw,vh,rem,em,calc(...),clamp(min, pref, max), CSS Variables (--my-var,var(--my-var, fallback)) - Visuals & Clipping:
color,background-color(#hex,rgb(),rgba(),linear-gradient(...)),border,border-radius(per-corner support),box-shadow,opacity,overflow: visible | hidden | clip | scroll | auto(with automatic scrollbar rendering) - Typography:
font-size,font-weight: normal | bold,line-height,text-align: left | center | right,text-transform: uppercase - Transforms & Animations:
transform(translate,translateX,translateY,scale,rotate),transform-origin,transition,transition-delay,transition-timing-function(linear,ease,ease-in,ease-out,ease-in-out),animation,@keyframes - Selectors & Pseudo-Classes: Tag,
#id,.class,[attr=val],:not(...),:nth-child(n),:hover,:active,:focus,:checked,:disabled,::before,::after, and combinators (,>,+,~)
🔄 Declarative Data Binding & Pub/Sub
Publish data from the server using ServerPubSubManager:
Map<String, Object> status = Map.of("energy", "8500 FE", "progress", "85%");
ServerPubSubManager.publish("machine_status", status);
Bind it directly in HTML using data-channel and data-bind (or data-list for virtualized scrolling lists):
<div data-channel="machine_status">
<span data-bind="text:energy">0 FE</span>
<div style="width: 100px; height: 10px; background: #222;">
<div data-bind="style.width:progress" style="height: 100%; background: #00ff88;"></div>
</div>
</div>
<!-- Virtualized List (renders only visible rows in DOM) -->
<div data-channel="storage_items" data-list="storage_items" data-item-width="48" data-item-height="48" style="height: 200px; overflow-y: scroll;">
<template>
<div class="slot">
<img src="item:{{id}}" />
<span>{{count}}</span>
</div>
</template>
</div>
🕶️ AR HUD & 3D World Entity Trackers
EnderFrameLib can render dynamic HUDs when looking at blocks/entities (data-hud) and floating 3D status bars above entities in the world (data-tracker), including server-side NBT inspection:
<!-- Crosshair Target HUD -->
<div id="look_hud" data-hud="true" data-hud-display="flex" data-hud-require-nbt="ForgeEnergy,Items">
<h2>{{name}}</h2>
<p data-show="{{is_entity}}">HP: {{hp}} / {{max_hp}}</p>
<p data-show="{{is_block}}">Hardness: {{hardness}}</p>
</div>
<!-- 3D Floating Entity Tracker -->
<div data-tracker="true" data-tracker-radius="24.0" data-tracker-y-offset="0.8" data-tracker-type="living">
<span>{{name}} ({{hp_percent}}%)</span>
</div>
📦 Package Architecture
com.nishiyu.enderframelib.api: Host interfaces (IWebScreenBlockEntity,IWebSessionProvider).com.nishiyu.enderframelib.core.dom: Tokenizer, parser,HtmlNode,DomUtils,DomDiffEngine, and NBT serializer.com.nishiyu.enderframelib.core.css:CssParser, specificity calculation, selector matching,calc()evaluator, and style computation.com.nishiyu.enderframelib.core.layout:LayoutBox,BlockLayoutEngine,FlexLayoutEngine, andGridLayoutEngine.com.nishiyu.enderframelib.client:ClientWebManager,ClientScreenInteractionManager,DeclarativeBindingManager,ClientPubSubManager, GUI screens (AbstractWebScreen,InvisibleInputScreen), and renderers (UIRenderer,WebRenderCore,WebAnimator,WebMediaManager,WebBlockRenderer,WebItemRendererHelper,WebHudRenderer).com.nishiyu.enderframelib.server:WebSession,WebSessionManager,ServerPubSubManager, andServerTargetSyncHandler.com.nishiyu.enderframelib.network: S2C/C2S payload definitions and rate-limiting handlers.

