⚠️ Note: The Steam Workshop version is currently unavailable because it was removed by Facepunch for breaking a rule.
I’m currently looking into a way to make it work without modifyingmenu.lua, if possible.
- Switch between Dark and Light visual themes for the entire main menu UI
- Background manager - enable/disable backgrounds by category, add custom images via URL, control slideshow speed and overlay dim level
- Automatically detects and categorizes backgrounds from all mounted Workshop addons
- Music player - plays MP3 and WAV files from local folders or Workshop addons, with playlist mode, shuffle, and volume control
- Album system - organize tracks into subfolders, each subfolder becomes a toggleable album
- Reads ID3 tags (artist, title, cover art) automatically from MP3 files
- Custom metadata via
data_static/te_music_meta.json(Workshop) ordata/theme_engine_music/te_music_meta.json(local) - Spawnmenu skin manager - switch between installed Derma skin addons from within the Theme Engine UI
- Custom font picker - override the menu font with built-in presets or locally installed
.ttffiles - Dark theme applied to the map loading screen
- Background and music preview modals with right-click
- Built-in Help system with categorized documentation for all features
Subscribe on the Steam Workshop.
The Theme Engine requires a loader to be added to Garry's Mod's menu.lua.
- Right-click Garry's Mod in Steam → Manage → Browse Local Files
- Navigate to
garrysmod/lua/menu/ - Open
menu.luawith any text editor - Go to this Pastebin
- Copy ALL the code from the Pastebin
- Replace the entire content of
menu.luawith the copied code - Save and restart Garry's Mod
A new Theme Options button will appear below the Options button in the main menu.
The entry point. On startup it scans for all theme_engine/*.lua files from the Workshop addon or local LUA path, then loads them in alphabetical order with theme_engine_injection.lua forced last. Each file is wrapped in pcall so a single error does not crash the menu. A GameContentChanged hook handles hot-reloading when the addon is enabled or disabled at runtime.
Provides DarkThemeEngine.CallJS(), the bridge between Lua and the CEF menu panel. Also contains ApplyTheme() which injects or removes the dark/light CSS stylesheets, and ApplyLoadingCSS() which styles the map loading screen.
Defines all CSS strings for the dark theme (menu, navbar, new game, server browser, workshop), the light mode extra, and the loading screen dark style. Editing this file changes the visual output of the theme without touching any logic.
Scans all mounted addons and local paths for background images, builds a categorized list, and caches it to data/theme_engine_data/backgrounds_cache.json for fast startup. Manages per-background and per-category enable/disable state. Overrides GMod's DrawBackground, ChangeBackground, and AddBackgroundImage globals to take control of the background rendering loop.
Scans sound/theme_engine_music/ (GAME and MOD paths) and data/theme_engine_music/ for audio files. Reads ID3 tags from MP3 files. Loads metadata from te_music_meta.json. Sends the track list to the JS player via SendMusicToJS(). Polls in-game state every second to pause music when a map is loaded and resume when returning to the menu.
Handles the Miscellaneous tab: spawnmenu skin detection (scans Lua files for derma.DefineSkin calls), custom background downloads via http.Fetch, and font loading from data/theme_engine_fonts/.
Defines DarkThemeEngine.Changelog and DarkThemeEngine.Credits tables. Edit this file to add new changelog entries - the in-game Changelog modal reads directly from it.
Contains DarkThemeEngine._UI.HTML - the full HTML/CSS template for the Theme Options page. This is the Angular template registered at the #/theme/ route.
Contains DarkThemeEngine._UI.JS - all client-side JavaScript logic for the UI: tab switching, background/music/spawnmenu/font rendering, preview modals, the Help system, and the Changelog panel. Also contains BuildRouteJS() which registers the Angular route, and AnniversaryJS for the November 29 event.
The final file loaded. Hooks pnlMainMenu.HTML.OnDocumentReady to inject the JS, CSS, route registration, and initial data in the correct order. Falls back to a 1-second retry via MenuStart if the hook fires too early.
Loaded via autorun/theme_engine_loader.lua. Reads the selected spawnmenu skin from settings.json and applies it in-game by overriding the ForceDermaSkin hook. Polls every 2 seconds to pick up changes made in the Theme Engine UI.
- Each file is loaded inside
pcall- a single error does not crash the menu - All JS hooks (
lua.Run,SetLastMap,UpdateServerSettings) are wrapped with idempotent guards to prevent double-hooking on re-injection - All fullscreen modals are removed automatically on page navigation
DarkThemeEngine.SaveSettings()is wrapped inpcall- a disk error does not crash the addon
Made with ❤️ by RemedyDev