Creating HUD Apps
How to create UI apps (HUD widgets) for BeamNG.drive — directory structure, AngularJS directives, receiving Lua data, and styling.
UI apps are draggable HUD widgets that players add to their screen — speedometers, debug panels, mission trackers, or anything your mod needs to display. They're built with AngularJS directives and communicate with your Lua code through the UI bridge.
Directory Structure
ui/modules/apps/mymod-hud/
app.js -- AngularJS directive
app.json -- metadata
app.css -- styles (optional)Place this inside your mod's zip or folder overlay.
app.json
Defines the app metadata:
{
"name": "My Mod HUD",
"author": "Your Name",
"version": "1.0",
"description": "Shows mod info on screen",
"directive": "myModHud",
"categories": ["debug"],
"width": 300,
"height": 200
}The directive field must match the AngularJS directive name in app.js.
app.js
A minimal UI app:
angular.module('beamng.apps')
.directive('myModHud', ['bngApi', function (bngApi) {
return {
restrict: 'E',
template: '<div class="my-mod-hud">' +
'<span>Score: {{score}}</span>' +
'</div>',
link: function (scope, element) {
scope.score = 0
// Receive data from Lua
scope.$on('MyModScoreUpdate', function (event, data) {
scope.$apply(function () {
scope.score = data.score
})
})
// Send command to Lua
scope.resetScore = function () {
bngApi.engineLua('extensions.mymod_hud.resetScore()')
}
// Clean up listener on destroy
scope.$on('$destroy', function () {
// AngularJS handles $on cleanup automatically
})
}
}
}])Receiving Data from Lua
In your GE extension, push data to the UI:
local M = {}
local function updateScore(score)
guihooks.trigger('MyModScoreUpdate', { score = score })
end
M.updateScore = updateScore
return MIn the app, listen with scope.$on():
scope.$on('MyModScoreUpdate', function (event, data) {
scope.$apply(function () {
scope.score = data.score
})
})Always wrap scope changes in scope.$apply() when they come from outside AngularJS.
Sending Commands to Lua
Use bngApi.engineLua() to call extension functions:
// Simple call
bngApi.engineLua('extensions.mymod_hud.doThing()')
// With arguments
bngApi.engineLua('extensions.mymod_hud.setValue(' + bngApi.serializeToLua(value) + ')')Styling
Add an app.css file or use inline styles. Apps render inside the game's CEF browser, so standard CSS works.
.my-mod-hud {
color: #fff;
font-family: 'Roboto', sans-serif;
padding: 8px;
background: rgba(0, 0, 0, 0.6);
border-radius: 4px;
}Tips for styling:
- Use
rgbabackgrounds for transparency over the game view - Light text on dark backgrounds for readability
- Keep widgets compact - screen space is limited
- Test at different resolutions
Tips
:::tip[Testing Apps]
After making changes to your app files, call reloadUI() from the Lua console to see changes without restarting the game.
:::
- The
directivename inapp.jsonmust exactly match the directive name inapp.js - Directive names are camelCase in JS, but the HTML element is kebab-case (
myModHudbecomes<my-mod-hud>) - Use
scope.$on('$destroy', ...)to clean up intervals or external listeners - Apps can be resized by the player - design for flexible dimensions
See Also
- UI Bridge - How Lua communicates with the UI layer
- GUI Hooks - Sending events to the UI
- Cross-VM Comms - Full communication patterns