Two gases#
There are two balloons, and they do opposite things to the voice.
| Balloon | Gas | Voice |
|---|---|---|
helium_balloon | Helium | Pitched up, squeaky |
sf6_balloon | Sulfur hexafluoride | Pitched down, deep |
Both work exactly the same way, only the voice differs. A single tank refills both. A third gas can be added with one line in Gases (id, balloon item, pitch), it then uses the red balloon visuals unless you give it its own model in shared/internal.lua.
Balloon in hand#
Using a balloon item does not instantly inhale it. The balloon is taken into the player's hand and a key hint box appears on the right side of the screen.
| Key | Action |
|---|---|
E | Take a breath, consumes gas and starts the voice effect |
G | Lift the balloon to your mouth or lower it back to your hand |
X | Put the balloon away |
R | Let the balloon go. The knot slips, it shoots off in spirals while it deflates with a real escaping air sound, and the item is gone |
The box also shows which gas is in the balloon, how much is left, and how long the voice effect still has to run. Using the item again while holding puts the balloon away, and using the other balloon swaps what is in your hand. While standing next to a placed tank, E and G are reserved for the tank so a refill cannot accidentally trigger a breath.
The balloon is dropped automatically when the player dies, blacks out, or when the item leaves their inventory.
The two poses use separate offsets, so the balloon hangs naturally in the hand and sits correctly at the mouth. Both poses are visible to every other player.
Balloon gas#
Every balloon carries its own gas amount in item metadata. The amount survives dropping, trading and server restarts.
- Each breath consumes
Balloon.gasPerInhale(default 25), so a full balloon is good for 4 breaths - The balloon visibly deflates as the gas drops: its model carries a baked inflation animation and the script freezes it on the frame that matches the gas, sliding along it during refills and breaths
- Every other player sees the exact same shape, the value is synced with a state bag
- One refill always tops the balloon up to 100%, only the missing amount is taken from the tank
- Inventories show the gas as the quality/durability bar and in the item description
Gas tank#
The tank is a real object placed in the world, not a static prop. There is only one tank item and it refills whichever balloon the player is holding, so a helium balloon comes back full of helium and an SF6 balloon full of SF6.
Placing
Using the tank item opens a placement mode. A semi transparent tank follows where you look:
| Key | Action |
|---|---|
ENTER | Place the tank here |
X | Cancel |
Mouse wheel | Rotate |
The tank cannot be placed further than about 4 meters away, the hint box turns red when you aim too far.
Interacting
Once placed, everyone nearby gets target options on the tank:
| Option | Who can use it |
|---|---|
| Refill balloon from tank | Everyone |
| Take the tank | Only the player who placed it, unless Tank.pickupAnyone is on |
| Check the tank | Everyone, shows the remaining gas percentage |
Without ox_target or qb-target the script falls back to key based interaction: E to refill, H to pick up.
Tank gas
The tank has its own supply (Tank.capacity, default 1000 units = 10 empty balloons). It drains with every refill and is removed when empty. Only one player can use a tank at a time, so two people cannot drain the same unit twice.
Placed tanks live only in memory. A resource or server restart removes every tank that is on the ground, so players should pick theirs back up before a restart.
Overdose system#
Every breath adds dose. Dose decays over time, so spacing breaths out keeps the player fine while chain inhaling gets dangerous.
| Stage | Default threshold | Effect |
|---|---|---|
| Dizzy | 30 | Light screen effect, camera sway, slightly drunk walk |
| Heavy | 60 | Strong screen effect, motion blur, very drunk walk, random stumbles to the ground |
| Blackout | 90 | Screen fades out, the player ragdolls on the ground, loses health, drops the balloon |
After waking up the dose is lowered to 45 so players cannot chain blackouts.
Dose is tracked server side. The client only plays the effects, so it cannot be faked.
Voice effect#
Two systems, picked automatically per player.
Real pitch shift (default)
While the effect is active the player's microphone is captured inside the FiveM NUI browser, pitch shifted with a phase vocoder and streamed to nearby players in 3D. This is a genuine pitch shift, the same thing a pitch shifter plugin does in a DAW, and it works in both directions: helium shifts up, SF6 shifts down.
- Nothing has to be installed, everything runs inside the resource
- FiveM asks the player once to allow microphone access, the answer is remembered per server
- By default (
Voice.requireMic) a balloon can only be used after the microphone was allowed, so everyone on the server is heard with the real pitch shift - Audio is only sent while the player is actually talking, push to talk and voice activation both work
- The pitch follows the dose, a first breath is light and a near blackout is the full effect
Built in effect (fallback)
Only used when Voice.requireMic is turned off: players who deny the microphone prompt then get the game's own RadioFx submix effect applied to their voice instead. It is not a real pitch shift but it still sounds altered.
The Voice System page explains how it works in detail.
Framework and inventory support#
Everything is detected automatically, no configuration needed.
| Layer | Supported |
|---|---|
| Framework | QBCore, QBox, ESX |
| Inventory | ox_inventory, qb-inventory and all qb forks (ps, lj, aj), qs-inventory, codem-inventory, tgiann-inventory, core_inventory, origen_inventory |
| Target | ox_target, qb-target, or a built in key fallback |
| UI | ox_lib, with a built in drawn box as fallback |
| Languages | Arabic, German, English, Spanish, French, Italian, Portuguese, Russian, Turkish |
Performance#
| State | Cost |
|---|---|
| Idle, nobody using anything | ~0.01 ms |
| Holding a balloon | ~0.05 ms |
| Placing a tank | ~0.08 ms |
Player scanning is event driven through state bags instead of polling loops, and the placement preview only updates the ghost object when it actually moves.
Exports#
Server
exports['frkn-helium']:giveHeliumVoice(source, 30000) -- timed effect in ms, nil uses the config value
exports['frkn-helium']:giveHeliumVoice(source, 30000, 'sf6') -- same but with the deep voice, gas id from FRKN.Gases
exports['frkn-helium']:setHeliumVoice(source, true) -- permanent on, false turns it off
exports['frkn-helium']:setHeliumVoice(source, true, 'sf6') -- permanent deep voice
exports['frkn-helium']:getDose(source) -- current dose, 0 to 100
exports['frkn-helium']:addDose(source, 34) -- add dose, returns true if it caused a blackout
The third argument is the gas id. Leaving it out uses the first gas in FRKN.Gases, which is helium.
Client
exports['frkn-helium']:getDose() -- local player dose, 0 to 100
Use the exports to hook the helium voice into other scripts, for example a drug script, a party event or an admin menu.