Skip to content

Client JS API

Client resources use JavaScript and the global varion object. The runtime injects globalThis.varion (also reachable as plain varion) before your client-main runs.

This page is the full reference for the client runtime API, grouped by namespace. See also WebView, GTA natives and the short client overview.

Terminology: client events are local events inside the client; server events are events exchanged with the server. There is no varion.onClient on the client — you receive events from the server via varion.onServer, and onClient exists only on the server side.


Logging

javascript
varion.log('message');    // info
varion.warn('message');   // warning
varion.error('message');  // error

varion.warn and varion.error are the only level helpers (logWarning / logError do not exist).


Events

Local client events

javascript
const sub = varion.on('myEvent', (a, b) => varion.log(`${a} ${b}`));
varion.once('resourceStop', () => varion.log('stopping'));
varion.off('myEvent', handler);
sub.destroy();               // on/once return { destroy() }

varion.emit('myEvent', 1, 2);        // local emit for other client handlers
varion.emitRaw('anyEvent', payload); // no reserved-name filter (for tests)

varion.emit blocks engine-reserved names (connection:*, stream:*, playerSpawn, entityStreamIn, etc.) and varion:-prefixed names. Listen to those with varion.on; only the engine may emit them.

Server events

javascript
varion.onServer('chat:addMessage', (author, text) => {});
varion.onceServer('welcome', (data) => {});
varion.offServer('chat:addMessage', handler);

varion.emitServer('chat:message', 'hello');
varion.emitServerUnreliable('position:update', x, y, z);

// Unreliable receive
varion.onServerUnreliable('position:update', (x, y, z) => {});
varion.offServerUnreliable('position:update', handler);
varion.onceServerUnreliable('position:update', handler);

emitServer arguments are auto-encoded into typed MValues (see below).

Listener introspection

javascript
varion.getLocalEventListeners('myEvent');            // local
varion.getRemoteEventListeners('chat:addMessage');   // server (reliable)
varion.getUnreliableRemoteEventListeners('pos');     // server (unreliable)

Tick loop

javascript
varion.everyTick(() => { /* ~every game tick */ });
varion.on('everyTick', handler);   // equivalent

ColShape events (casing matters)

The client uses exactly this casing:

EventArguments
playerEnterColShape(colShape)
playerExitColShape(colShape)
entityEnterColshape(colShape, entity)
entityLeaveColshape(colShape, entity)

Note: playerEnter/ExitColShape use a capital S, while entityEnter/LeaveColshape use a lowercase s (and Leave, not Exit).


RPC

javascript
// Call a server RPC and await the response (Promise)
const result = await varion.callServer('server:ping', arg);
const same   = await varion.emitRpc('server:ping', arg);   // emitRpc is an alias of callServer

// Client RPC handler (server calls it via EmitClientRpcAsync)
varion.onRpc('client:ping', async () => ({ pong: true }));
varion.offRpc('client:ping', handler);

callServer / emitRpc return a Promise that resolves with the server result or rejects on error/timeout.


MValue

The typed-value factory varion.MValue (applied automatically by emitServer, but also available explicitly):

javascript
varion.MValue.nil();
varion.MValue.bool(true);
varion.MValue.int(-5);
varion.MValue.uint(10);
varion.MValue.double(1.5);
varion.MValue.string('hi');
varion.MValue.list(a, b, c);
varion.MValue.dict({ k: 'v' });
varion.MValue.vector3(x, y, z);
varion.MValue.entity(entityType, id);
varion.MValue.rgba(255, 0, 0, 255);
varion.MValue.byteArray(str);
varion.MValue.from(anyValue);   // auto-detect type
varion.MValue.to(mvalue);

Hashing and model loading

javascript
const hash = varion.hash('mp_m_freemode_01');   // joaat is an alias of hash
varion.joaat('adder');

varion.loadModel('adder');
varion.isModelLoaded('adder');       // → boolean
varion.unloadModel('adder');
await varion.waitForModel('adder', 10000);   // Promise, resolves the hash

varion.requestIpl('v_janitor');
varion.removeIpl('v_janitor');

Timers

Tick-driven timers (pumped on the game tick). Available both as varion.* methods and as global functions.

javascript
const t = varion.setTimeout(() => {}, 500);
varion.clearTimeout(t);

const i = varion.setInterval(() => {}, 1000);
varion.clearInterval(i);

setTimeout(fn, 500);   // global aliases are available too

A resource's timers are cleared automatically when the resource stops.


Cursor and input

javascript
varion.isKeyDown(varion.KeyCode.E);     // by code or name
varion.showCursor(true);                // show/hide the cursor
varion.getCursorPos(true);              // { x, y } (true = normalized 0–1)
varion.setCursorPos({ x, y }, true);
varion.toggleGameControls(false);       // disable game controls
varion.gameControlsEnabled();           // → boolean
  • varion.KeyCode (alias varion.Key) maps key names to codes (AZ, F1F12, Numpad0Numpad9, Shift, Ctrl, Space, MouseLeft, etc.).
  • varion.GamepadButton maps gamepad buttons (A, B, X, Y, LB, RB, DPadUp, etc.).

Keybind

javascript
const kb = new varion.Keybind(varion.KeyCode.E, /* hold */ false);
kb.on('pressed', () => varion.log('E down'));
kb.on('released', () => varion.log('E up'));
kb.gameControlsOnly = true;   // ignore while game controls are disabled
kb.enableInUi = false;        // ignore while a WebView is focused / cursor shown
kb.repeatIntervalMs = 0;      // >0 — auto-repeat while held
kb.isPressed;                 // current state
kb.destroy();

Entities

BaseObjectType

javascript
varion.BaseObjectType
// { Player:0, Vehicle:1, Ped:2, Object:3, Blip:4, ColShape:7,
//   Checkpoint:8, VirtualEntity:9, Marker:22, TextLabel:23 }

Base Entity

javascript
varion.Entity.getByID(type, id);
varion.Entity.getByRemoteID(type, remoteId);
varion.Entity.getByScriptID(scriptId);
varion.Entity.all;

Common instance getters: id, remoteId, isRemote, type, scriptID, valid, isStreamedIn, streamed, isSpawned, dimension, isDead, speed, visible, alpha, nativeModel, heading, pos, rot.

Instance methods:

javascript
entity.exists();
entity.destroy();
entity.setPos(x, y, z);
entity.setRot(x, y, z);
await entity.setVisible(true);
await entity.setAlpha(200, false);
await entity.resetAlpha();
entity.attachTo(target, bone, offset, rotation, useSoftPinning, collision, fixedRot);
entity.detach();
entity.setCollision(true, true);
await entity.freezePosition(true);
entity.getVariable(key);        // synced meta
entity.hasVariable(key);
await entity.isDeadAsync();     // exact read from the game thread
await entity.getSpeedAsync();
await entity.existsAsync();

setPos/setRot/destroy on other players are server-authoritative and return false.

Player

javascript
const me = varion.Player.local;
varion.Player.get(id);
varion.Player.getByID(id);
varion.Player.getByRemoteID(id);
varion.Player.getByScriptID(scriptId);
varion.Player.all;         // includes the local player
varion.Player.streamedIn;  // streamed-in only
varion.Player.count;
varion.getPlayers();       // = Player.all

Key player getters: name (remote), model, health, armour, currentWeapon, currentAmmo, currentWeaponComponents, currentWeaponTintIndex, moveSpeed, forwardSpeed, strafeSpeed, speedVector, aimPos, headRot, entityAimingAt, vehicle, seat, netOwner, isTalking, micLevel, plus state flags isAiming, isJumping, isShooting, isCrouching, isStealthy, isInRagdoll, isReloading, isEnteringVehicle, isLeavingVehicle, isOnLadder, isInMelee, isInCover, isParachuting, flashlightActive.

Player.local also exposes: weapon, ammoInClip, interiorId, maxHealth, maxArmour, dimension, isDead, isSpawned. Methods: hasWeaponComponent(weapon, component), getWeaponTintIndex(weapon).

Vehicle, Object, Ped

javascript
varion.Vehicle.get(id);        // .all / .streamedIn / .count / .getByID /
                               // .getByRemoteID / .getByScriptID
varion.getVehicles();

varion.Object.get(id);         // same set of statics
varion.getObjects();

varion.Ped.get(id);            // same set of statics
varion.getPeds();

Vehicle getters: model, speedVector, engineOn, engineHealth, petrolTankHealth, gear, rpm, steeringAngle, lockState, isDestroyed, handling, netOwner.

VirtualEntity

javascript
varion.VirtualEntity.get(id);          // .all / .streamedIn / .getByRemoteID
varion.VirtualEntityGroup.get(id);     // .all
varion.VirtualEntityGroup.cellKey(x, y, 100);

Dimension-scoped queries

javascript
varion.getPlayersInDimension(dim);
varion.getVehiclesInDimension(dim);
varion.getObjectsInDimension(dim);
varion.getPedsInDimension(dim);
varion.getEntitiesInDimension(dim);

Entity metadata

javascript
entity.getMeta(key);        entity.setMeta(key, value);   // local meta
entity.hasMeta(key);        entity.deleteMeta(key);
entity.getSyncedMeta(key);  entity.hasSyncedMeta(key);    // server-synced
entity.getStreamSyncedMeta(key);                          // stream-synced
entity.getMetaDataKeys();   entity.getSyncedMetaKeys();

Local entities (client only)

Created and owned entirely on the client, with no network ID and no server synchronization. Useful for decorative peds, objects and vehicles.

javascript
const ped = new varion.LocalPed(model, { x, y, z }, { x, y, z });
const obj = new varion.LocalObject(model, { x, y, z }, { x, y, z }, /* noOffset */ false);
const veh = new varion.LocalVehicle(model, { x, y, z }, { x, y, z });

obj.pos = { x, y, z };   // pos / rot / frozen / collision / visible are get/set
obj.frozen = false;
obj.destroy();

About scriptID: right after the constructor scriptID is 0 and id is negative (a local id). Once the engine commits the entity into the world, scriptID becomes a non-zero GTA handle and the gameEntityCreate / baseObjectCreate events fire. Check entity.isSpawned or wait for gameEntityCreate before calling native handles by scriptID.

varion.WeaponObject(model, pos, rot, ammo, showWorldModel) is a separate local weapon object with a destroy() method.


World objects

The Blip, Marker, Checkpoint, ColShape, TextLabel classes share statics all, count, get(id), getByID(id), getByRemoteID(id) (Blip also has getByScriptID).

javascript
const blip = varion.Blip.get(id);
blip.sprite = 1; blip.color = 5; blip.scale = 1.2;   // get/set
blip.name = 'Shop'; blip.shortRange = true; blip.route = false;
blip.pos = { x, y, z };                              // + radius/width/height, isRadius/isArea

const mk = varion.Marker.get(id);
mk.markerType = 1; mk.scale = 2; mk.color = { r, g, b, a };

const cp = varion.Checkpoint.get(id);
cp.checkpointType = 1; cp.radius = 3; cp.height = 2; cp.color = { r, g, b, a };

const cs = varion.ColShape.get(id);       // colshapeType / radius / height /
                                          // halfExtents / points / pos / dimension

// Local 3D text:
const label = new varion.TextLabel('Hello', 0, { x, y, z }, 1, { r:255,g:255,b:255,a:255 }, true, 0);
label.destroy();

Global and local metadata

javascript
// Local (synthetic, client only)
varion.setMeta(key, value);  varion.getMeta(key);  varion.hasMeta(key);
varion.deleteMeta(key);      varion.getMetaKeys();

// Server-synced global (read-only on the client)
varion.getSyncedMeta(key);   varion.hasSyncedMeta(key);   varion.getSyncedMetaKeys();

// Local player meta
varion.setLocalMeta(key, value);  varion.getLocalMeta(key);
varion.hasLocalMeta(key);         varion.deleteLocalMeta(key);  varion.getLocalMetaKeys();

// Request a stream-synced meta set (sent to the server)
varion.setStreamSyncedMeta(key, value);
varion.deleteStreamSyncedMeta(key);

Voice

javascript
varion.Voice.volume = 1;              // 0–2
varion.Voice.muteInput = true;        // mute your own microphone
varion.Voice.activationLevel = 37;    // 0–100 (voice activation)
varion.Voice.noiseSuppressionEnabled = true;
varion.Voice.inputDevice = deviceUuid;
varion.Voice.activityInputEnabled = true;
varion.Voice.toggleInput(true);       // manual push-to-talk
varion.Voice.mutePlayer(player);      varion.Voice.unmutePlayer(player);
varion.Voice.isPlayerTalking(player); varion.Voice.isPlayerMuted(player);
varion.Voice.getPlayersInChannel(channelId);
varion.Voice.getAvailableInputDevices();
varion.Voice.clearLocallyMuted();

const ch = varion.Voice.getChannel(channelId);   // or varion.VoiceChannel.getByID(id)
ch.spatial; ch.maxDistance; ch.volume; ch.members; ch.muted; ch.filter;
ch.getPlayers(); ch.isActive();

const filter = varion.AudioFilter.create('echo');   // by name or hash

varion.muteInput(true);
varion.toggleVoiceControls(true);
varion.clearLocallyMuted();

Audio

javascript
const snd = varion.Audio.create(varion.assetUrl('mymode', 'sfx/click.wav'), 1);
snd.loop = false; snd.category = 'sfx';
await snd.play();  snd.pause();  snd.stop();  snd.seek(2);
snd.setVolume(0.5); snd.destroy();
snd.playing; snd.currentTime; snd.duration; snd.playbackRate;

varion.Audio.playFrontend(url, 1);      // frontend (2D) sound

// 3D sound: add a world output
snd.addOutput(new varion.AudioOutputWorld({ x, y, z }, 50));

const cat = varion.AudioCategory.get('sfx');  cat.volume = 0.8;

HTTP

javascript
const res = await varion.Http.get('/server-info.json');
// { ok, status, statusCode, url, text, body, headers }
const data = await varion.Http.getJson('/data.json');   // + res.json

const r = await varion.fetch('/api', { method: 'POST', body: JSON.stringify({}) });

const client = varion.HttpClient.create();
client.setExtraHeader('X-Auth', token);
await client.get(url);   // + head/post/put/delete/patch/options/connect/trace

Requests are allowed only to the host/port of the resource's manifest HTTP server or its asset-pack; the game port 7788 is blocked.


Resources

javascript
varion.Resource.all;                    // all resources
varion.Resource.current;                // current one (in execution context)
varion.Resource.getByName('mymode');    // or varion.Resource.get('mymode')
varion.Resource.resolveAsset('mymode', 'ui/index.html');
varion.Resource.getPath('mymode', 'client/main.js');

varion.getResources();                  // = Resource.all
varion.getResourceConfig('mymode');     // config object from the manifest
varion.getResourcePath('mymode', 'client/main.js');
varion.hasResource('mymode');

// Inter-resource exports (client-local, no server)
varion.exports.foo = () => 42;                    // register on YOUR resource
varion.exports = { foo, bar };                    // or in bulk
varion.getResourceExports('other').foo();         // call another resource's export
const other = varion.require('other');            // throws if the resource is not found

URLs and assets

javascript
varion.httpUrl('cef/index.html');                 // resource's base HTTP server
varion.assetUrl('mymode', 'ui/index.html');       // resource asset-pack
varion.streamUrl('mymode', 'vehicles/adder.ytd'); // resource stream assets
varion.streamCachePath('mymode', 'file.ytd');     // path in the local stream cache

Storage

javascript
varion.LocalStorage.set('token', 'value');
varion.LocalStorage.get('token');
varion.LocalStorage.has('token');
varion.LocalStorage.delete('token');   // = remove
varion.LocalStorage.clear();           // = deleteAll
varion.LocalStorage.save();            // persist to disk

LocalStorage is saved automatically on resourceStop.


Files

javascript
await varion.File.write('cache:save.json', JSON.stringify(data));
const text = await varion.File.read('cache:save.json', 'utf8');
await varion.File.exists('cache:save.json');   // → boolean
await varion.File.delete('cache:save.json');
varion.File.resolvePath('resource:mymode/data.json');

varion.readFile(path);          // aliases
varion.writeFile(path, data);

Camera and screen

javascript
varion.getCamPos();            // { x, y, z }
varion.getCamRot();            // { x, y, z }
varion.getCamFov();            // number
varion.getCamForwardVector();  // { x, y, z }
varion.getScreenResolution();  // { x, y }

varion.screenToWorld(0.5, 0.5, 100);              // synchronous (ray)
await varion.screenToWorldAsync(0.5, 0.5, 500);   // exact game-thread pick
await varion.worldToScreenAsync(x, y, z);         // { x, y, z } normalized
varion.worldToScreen(x, y, z);                    // synchronous (from cache)

varion.copyToClipboard('text');

Stats

javascript
await varion.getStat('MP0_CASH');           await varion.setStat('MP0_CASH', 1000);
await varion.getStatFloat('name');          await varion.setStatFloat('name', 1.5);
await varion.getStatBool('name');           await varion.setStatBool('name', true);
varion.resetStat('name');                   varion.resetAllStats(0);

varion.getPackedStat(index, 0);             varion.setPackedStat(index, value, 0);
varion.getPackedStatBool(index, 0);         varion.setPackedStatBool(index, value, 0);

getStat* / setStat* go through the game bridge and return a Promise.


Permissions and screenshots

javascript
varion.Permission;        // { None:0, ScreenCapture:1, WebRTC:2, Microphone:3, Clipboard:4, All:255 }
varion.PermissionState;   // { Allowed:0, Denied:1, Unspecified:2, Failed:3 }

varion.getPermissionState(varion.Permission.ScreenCapture);
varion.hasPermission(varion.Permission.Microphone);
const state = await varion.requestPermission(varion.Permission.ScreenCapture);

const base64 = await varion.takeScreenshot();          // whole screen (base64 PNG)
const gameOnly = await varion.takeScreenshotGameOnly(); // game frame only

takeScreenshot will request the ScreenCapture permission itself if needed.


WebSocket and Worker

javascript
const ws = new varion.WebSocketClient('wss://example/path');
ws.on('open', () => ws.send('hi'));
ws.on('message', (data) => {});
ws.on('close', (code, reason) => {});
ws.autoReconnect = true;
ws.addSubProtocol('json');  ws.setExtraHeader('X-Auth', token);
ws.start();  ws.stop();  ws.destroy();
varion.WebSocketReadyState;   // { CONNECTING, OPEN, CLOSING, CLOSED }

const worker = new varion.Worker(varion.assetUrl('mymode', 'worker.js'));
worker.on('message', (data) => {});
worker.start();  worker.pause();  worker.resume();  worker.destroy();

Discord

javascript
varion.Discord.isReady();          // readiness
varion.Discord.currentUser;        // { id, name, discriminator, avatar } | null
varion.Discord.updateActivity({ details: 'In game', state: 'Freeroam', largeImageKey: 'logo' });
varion.Discord.getActivity();
varion.Discord.clearActivity();
await varion.Discord.requestOAuth2Token(appId);
varion.on('discord:ready', (user) => {});

Handling and MapZoom

javascript
const hd = varion.HandlingData.getForName('adder');   // or .getForHash(hash)
hd.acceleration; hd.brakeForce; hd.maxTraction; /* ... */
hd.setField('acceleration', 0.5);
await hd.applyTo(vehicle);
varion.getVehicleHandling(vehicle);
varion.HandlingData.reloadVehiclePhysics(modelHash);

const zoom = varion.MapZoomData.get('MAIN_MAP');
zoom.fZoomScale = 1.2;
varion.MapZoomData.resetAll();

Server, connection and diagnostics

javascript
varion.getServerHost();  varion.getServerIp();  varion.getServerPort();  varion.getServerName();
varion.getPing();
varion.isConnected();
varion.getConnectionPhase();   // 'idle' | 'ready' | ...
varion.isPaused();  varion.isMenuOpen();  varion.isRadarHidden();
varion.getFps();  varion.getTotalPacketsSent();  varion.getTotalPacketsLost();

varion.getLocale();            // varion.locale
varion.LicenseType;            // { None, Rockstar, Steam, Epic, Xbox }
varion.getLicenseType();  varion.getLicenseHash();

varion.getGxtText('key');      // GXT label text

Weather and config flags

javascript
varion.setWeatherCycle(['CLEAR', 'RAIN'], [10, 5]);
varion.setWeatherSyncActive(true);
varion.setConfigFlag('flagName', true);
varion.getConfigFlag('flagName');
varion.loadYtyp('name');   varion.unloadYtyp('name');

Utilities

varion.Utils — vector math and helpers:

javascript
varion.Utils.distance(a, b);       varion.Utils.squaredDistance(a, b);
varion.Utils.vectorLength(v);      varion.Utils.normalize(v);
varion.Utils.dot(a, b);            varion.Utils.cross(a, b);
varion.Utils.add(a, b);            varion.Utils.sub(a, b);
varion.Utils.mul(v, s);            varion.Utils.divide(v, s);  varion.Utils.neg(v);
varion.Utils.lerp(a, b, t);        varion.Utils.clamp(v, min, max);
varion.Utils.degToRad(d);          varion.Utils.radToDeg(r);
varion.Utils.randomInt(min, max);  varion.Utils.isNaN(v);
await varion.Utils.wait(500);      // Promise delay
await varion.Utils.registerPedheadshotBase64(ped);   // + registerPedheadshot3Base64 / *Transparent*

const b64 = await varion.getHeadshotBase64(headshotId);

const p = new varion.Profiler();
p.timeStart('x');  const ms = p.timeEnd('x');
varion.Profiler.fps;  varion.Profiler.tickMs;  varion.Profiler.nativeCalls;

varion.getGamepads();          // array of Gamepad
varion.Gamepad.get(0);         // .connected / .axisLeftX / .isButtonPressed(btn)

UI: WebView and RmlDocument

The full WebView reference is on the WebView page.

javascript
const view = varion.WebView.create({ url: varion.assetUrl('ui-pack', 'index.html'), overlay: true });
view.visible = true;
view.on('hud:ready', () => {});
view.emit('hud:setHealth', 200);

// RmlDocument is HTML/WebView-backed (there is no native RmlUi renderer; a .html
// source loads into a WebView, a .rml source shows a notice):
const doc = new varion.RmlDocument(varion.assetUrl('ui-pack', 'menu.html'));
doc.show(false, true);   doc.hide();   doc.update();

Stream events

javascript
varion.on('stream:ready', (...mounts) => {
  // one argument per mounted resource
  for (const mount of mounts) varion.log(`${mount.name} -> ${mount.streamBase}`);
});
varion.on('stream:updated', (...mounts) => {});
varion.on('stream:mounted', (stats) => {});   // { registered, existing, failed, ... }

Each mount is an object { name, streamBase } (IStreamResourceMount). It is not a resource-name string; a full remount delivers every resource at once (for a single resource, a single object).


GTA Natives

javascript
// Call by name (camelCase)
varion.natives.setEntityVisible(scriptId, true, false);
varion.natives.freezeEntityPosition(scriptId, true);

// Call by hash
varion.natives.invoke('0x262B14F48D29DE80', pedHandle, 11, 1, 0, 0);
varion.natives.invokeFloat('0xEEF059FAD016D209', pedHandle);
varion.natives.invokeVector3('0x1EB9E7DF7E839CAF', x, y, z, heading, rx, ry, rz);

// Real return values come from the async variants
const health = await varion.natives.invokeAsync('GET_ENTITY_HEALTH', scriptId);

varion.game is the same bridge plus joaat. A synchronous invoke returns a placeholder for most read natives; for a real value use invokeAsync / invokeFloatAsync / invokeStringAsync / invokeVector3Async. See the GTA natives page for details.


Client JS must not be treated as trusted from a gameplay/security standpoint. Validate important gameplay, economy and permission actions on the server.

Source backing

This page is built from the runtime files sdk/client-js-runtime/injected/core-bootstrap.js, events-bootstrap.js, entities-bootstrap.js, services-bootstrap.js, world-bootstrap.js, interaction-bootstrap.js, platform-bootstrap.js, dispatch-bootstrap.js, mvalue-bootstrap.js and client/src/stream/CStreamResourceClient.cpp.

Varion Multiplayer Platform