A FiveM/GTA V server resource that integrates with the Medal.tv desktop client to capture gameplay clips and screenshots. It supports manual capture and optional automatic capture based on ingame events, configurable via an ingame menu.
- Auto-Clipping UI: Toggle auto-clipping, configure clip length, and enable/disable specific events.
- Custom Event Signals: Dynamically register your own clipping events at runtime via the
registerSignalexport. - SuperSoaker Screenshots: Drop-in replacement for
screenshot-basic, with both local capture, and HTTP upload. Features automatic fallback from Medal.tv to WebGL, if some unforeseen issue with the Medal connection occurs. Medal availability is monitored continuously (configurable interval), adapting in real-time, if Medal starts, stops, or crashes. - GameVein Data Pipeline: Allows the Medal client to get game context and server info when clipping.
- Framework-Aware: The server detects framework (ESX → QBX → QB → ND → OX → TMC → unknown) and provides safe export helpers to avoid errors, without loading any framework resource files directly.
- FiveM server (FX Server from after 2020-05)
- Medal.tv client installed, and running on the player’s PC (to use the full features)
- Node.js LTS with pnpm installed (just to build the UI, one time, or avoid this with a Github release).
-
Place this resource folder into your server's
resources/directory. -
In your server's
server.cfg(or whatever config file your server uses to load resources), add the line below (replace with your actual folder name for this resource):ensure medal--fivem-resource
-
Start (or restart) your server.
- Ensure the Medal.tv desktop client is running before joining the server.
- Toggle the Medal UI in-game:
- Chat command:
/medal(fromConfig.Command) - Default keybind:
Page Up(fromConfig.Keybind) - The keybind is shown in the UI using FiveM's current binding for the command (resolved via
GetHashKey+GetControlInstructionalButton), so if a player rebinds the key in FiveM, the UI label follows their current binding. - Send the command again, press the keybind again, press ESC, or click the X button to close the UI. The UI listens for the same key that opened it and closes on key press (keydown).
- Chat command:
- In the UI, you can:
- Toggle auto-clipping on/off
- Set clip length
- Enable/disable individual events
Settings persist via resource KVPs; toggles and clip length are remembered per player.
You can configure automatic capture triggers through the ingame menu. Common examples include:
- Player deaths/downs
- Kills or headshots
Your server can enable/disable specific events to fit its gameplay style.
This resource uses a pnpm workspace to manage builds for both the UI and the SuperSoaker HTTP server.
-
Node.js LTS (v18+)
-
pnpm installed globally:
npm i -g pnpm
From the root of the resource:
-
Install dependencies:
pnpm install
-
Build everything:
pnpm build
This builds:
- UI:
ui/src→ui/dist(Vite build for the NUI) - SuperSoaker Server:
superSoaker/src/server/server.ts→superSoaker/dist/server.js(TypeScript → CommonJS)
pnpm build:server- Build only the SuperSoaker HTTP serverpnpm build:ui- Build only the React UIpnpm dev:ui- Start Vite dev server for UI development
Note: The compiled superSoaker/dist/server.js must exist before starting the resource.
To create a production-ready release package for deployment to FiveM servers:
pnpm releaseTo build a fresh release and immediately verify the output in one step, run:
pnpm release:verifyIf you only need to run the verification checks against an existing release folder, use:
pnpm verify-releaseThese commands:
- Automatically builds the entire project (UI and TypeScript)
- Creates a
release/medal/directory - Packages only production files (~60 files, ~2.3 MB):
- Core resource files (
fxmanifest.lua,config.lua) - All Lua scripts (client/server/shared)
- Built JavaScript (
superSoaker/dist/server.js) - Built UI (
ui/dist/*) - Documentation (all README files, LICENSE)
- Core resource files (
The release script:
- Dynamically excludes files from
.gitignoreand.git/info/exclude - Preserves built
distfolders (overriding gitignore) as they contain production code - Works on Windows, Linux, and macOS
- Verifies the release contents to ensure required files ship and unwanted files stay excluded (
release:verifyorverify-release)
The resulting release/medal/ folder can be directly copied to your FiveM server's resources directory.
config.luaConfig.CommandandConfig.Keybindcontrol how players open the UI.Config.ClippingEventspre-registers events visible in the Auto-Clipping UI.Config.ScreenshotsMedalPreferred: Prefer Medal.tv for screenshots when available. If Medal is unable to respond (network errors, timeouts, etc.), the resource automatically falls back to WebGL/Three.js capture. Medal availability is checked periodically (default: 3 seconds, configurable viaConfig.Medal.CheckIntervalMs), allowing automatic adaptation if Medal starts, stops, or crashes during gameplay.ScreenshotBasicOverride: Provides compatibility forscreenshot-basicexports.
-
fillSoaker(options?: SoakerOptions, cb: fun(data:string))-
Capture a screenshot and return a Data URI to the callback.
-
Example:
--//=-- Local screenshot as Data URI exports['medal--fivem-resource']:fillSoaker({ encoding = 'jpg', quality = 0.92 }, function(data) TriggerEvent('chat:addMessage', { template = '<img src="{0}" style="max-width: 300px;" />', args = { data } }) end)
-
-
shootWater(url: string, field: string, options?: SoakerOptions, cb: fun(result:string))-
Capture a screenshot and upload as
multipart/form-datatourl, invoking the callback with the HTTP response text. -
Example:
--//=-- Upload screenshot exports['medal--fivem-resource']:shootWater('https://your-upload/endpoint', 'file', { encoding = 'jpg', quality = 0.9, headers = { Authorization = 'Bearer XYZ' } }, function(resp) print('upload result', resp) end)
-
-
registerSignal(event: string, options: EventConfig)-
Dynamically register a custom auto-clipping event with the UI and handler.
-
Example:
--//=-- Register a custom event; when `my:custom:event` is triggered, a clip request is sent exports['medal--fivem-resource']:registerSignal('my:custom:event', { id = 'my_custom_event', title = 'My Custom Event', desc = 'Triggers on my custom event', enabled = true, tags = { 'custom' } })
-
Event configs define how an auto-clipping event appears and behaves in the UI. See clipping/__types.lua.
EventConfig = {
id: string, -- unique id used internally and for persistence
title: string, -- display name in the UI
desc?: string, -- optional description shown in the UI
enabled?: boolean, -- whether the event is enabled by default
tags?: string[], -- optional tags passed along with clip requests
}requestPlayerWater(player: number, options: SoakerOptions, cb: fun(err:any|false, data:string, src:number))-
Ask a specific player to capture a screenshot; the callback receives a Data URI.
-
Example:
--//=-- Request a screenshot from a player exports['medal--fivem-resource']:requestPlayerWater(source, { encoding = 'png' }, function(err, data, src) if err then print('screenshot error', err) return end print(('player %s returned %d bytes'):format(src, #data)) end)
-
- When
Config.Screenshots.ScreenshotBasicOverride = true, the resource provides handlers compatible withscreenshot-basicclient/server exports (fill/upload and request client screenshot). Prefer thefillSoaker/shootWater/requestPlayerWaterexports shown above for consistent behavior.
-
Core
-
UI
-
Utilities
View All Contributors (Click Here)
If you encounter issues, or want to suggest new auto-clipping events, please open an issue or PR.