<!-- fivemad.com, Markdown source of https://fivemad.com/docs/development/runtimes -->
> Source: https://fivemad.com/docs/development/runtimes
> Section: Development
> Tags: lua, javascript, typescript, csharp, runtimes, natives
> Last updated: 2026-10-13
> Site map for assistants: https://fivemad.com/llms.txt

# Lua, JavaScript and C#: one script, three runtimes

FiveM runs three scripting runtimes. What a native call, a wait loop, a hash, a vector and an event look like in each, and where Lua has no clean equivalent.

FiveM ships three scripting runtimes and a resource can mix them: Lua, JavaScript on V8 (TypeScript compiles down to it) and C# on Mono. They all call the same natives under the same names, so most of a script looks the same in all three. The differences are in the ceremony around it: how you wait, how you hash a name, what a vector is, and where the code lives.

Every snippet block on this site has a **Lua / JS / TS / C#** switcher in its top left corner. Pick a runtime once and every snippet on every page follows it, so this page is the reference for what the switcher is doing.

## Calling a native

All three runtimes expose natives as PascalCase functions with the same name, generated from the same native list:

```lua title="Lua"
local ped = PlayerPedId()
SetEntityCoords(ped, 674.883, 548.269, 133.94, false, false, false, true)
```

```js title="JavaScript"
const ped = PlayerPedId();
SetEntityCoords(ped, 674.883, 548.269, 133.94, false, false, false, true);
```

```csharp title="C#"
using CitizenFX.Core;
using static CitizenFX.Core.Native.API;

int ped = PlayerPedId();
SetEntityCoords(ped, 674.883f, 548.269f, 133.94f, false, false, false, true);
```

> [!NOTE]
> In C# the natives live in `CitizenFX.Core.Native.API`. The `using static` line is what lets you write `SetEntityCoords(...)` instead of `API.SetEntityCoords(...)`. Float parameters need the `f` suffix.

There is a second, lower level C# form, `Function.Call(Hash.SET_ENTITY_COORDS, ...)`, with the raw `SCREAMING_SNAKE` native name as the enum member. It exists, but no official example uses it: the `API` wrappers are the documented way.

## Where the code lives

Lua and JavaScript run their top level code when the resource starts. C# has no top level code at all: a script is a class that derives from `BaseScript`, and its constructor is the "when the resource starts" hook.

```csharp title="C#"
public class MyScript : BaseScript
{
    public MyScript()
    {
        // runs when the resource starts
    }
}
```

## Waiting

This is the biggest difference.

| | How you yield |
| --- | --- |
| Lua | `Wait(0)` anywhere, including at the top level |
| JavaScript | no built-in wait; write `const Delay = (ms) => new Promise((res) => setTimeout(res, ms));` and `await` it |
| C# | `await Delay(0)`, which `BaseScript` provides, inside an `async Task` method |

```lua title="Lua"
RequestModel(`adder`)
while not HasModelLoaded(`adder`) do Wait(0) end
```

```js title="JavaScript"
const Delay = (ms) => new Promise((res) => setTimeout(res, ms));

(async () => {
    const model = GetHashKey("adder");
    RequestModel(model);
    while (!HasModelLoaded(model)) await Delay(0);
})();
```

The IIFE is there because top level `await` is not available in the V8 runtime.

```csharp title="C#"
public class SpawnCar : BaseScript
{
    public SpawnCar()
    {
        Tick += Run;
    }

    private async Task Run()
    {
        Tick -= Run; // run this once, not every frame
        var model = (uint)GetHashKey("adder");
        RequestModel(model);
        while (!HasModelLoaded(model)) await Delay(0);
    }
}
```

A `Tick` handler is the only place a `BaseScript` gets an `async` method, so a one-off task hooks `Tick`, unhooks itself on the first frame and carries on.

## Running every frame

Lua's `CreateThread` runs its body **once**. The per frame loop is the `while true do Wait(0) end` inside it. The other two runtimes have a real per frame hook, so there is no loop to write:

```lua title="Lua"
CreateThread(function()
    while true do
        Wait(0)
        DrawMarker(1, x, y, z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 255, 0, 0, 200, false, false, 2, false, nil, nil, false)
    end
end)
```

```js title="JavaScript"
setTick(async () => {
    DrawMarker(1, x, y, z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 255, 0, 0, 200, false, false, 2, false, null, null, false);
});
```

```csharp title="C#"
public MyScript()
{
    Tick += OnTick;
}

private async Task OnTick()
{
    DrawMarker(1, x, y, z, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 255, 0, 0, 200, false, false, 2, false, null, null, false);
}
```

> [!TIP]
> If a `setTick` callback returns a promise, the runtime skips that ticker until the promise resolves, so an `async` body never stacks up.

## Hashes

Lua has a compile time hash literal. Nothing else does.

```lua title="Lua"
local model = `prop_bench_01a`
```

```js title="JavaScript"
const model = GetHashKey("prop_bench_01a");
```

```csharp title="C#"
var model = (uint)GetHashKey("prop_bench_01a");
```

> [!IMPORTANT]
> The C# cast is not cosmetic. `GetHashKey` returns an `int` while every native parameter typed `Hash` takes a `uint`, so without `(uint)` it is a compile error.

In JavaScript you can also pass the name straight through wherever the parameter is a `Hash`, because the generated wrapper hashes a string for you. `GetHashKey` is spelled out here because it works everywhere, including parameters typed plain `int`.

## Vectors

A native that returns a vector gives you three different things:

```lua title="Lua"
local coords = GetEntityCoords(ped, false)
print(coords.x, coords.y, coords.z)
```

```js title="JavaScript"
const coords = GetEntityCoords(ped, false); // a plain [x, y, z] array
console.log(coords[0], coords[1], coords[2]);
```

```csharp title="C#"
Vector3 coords = GetEntityCoords(ped, false);
Debug.WriteLine($"{coords.X} {coords.Y} {coords.Z}");
```

Lua also has vector arithmetic, so `#(a - b)` is the distance between two points. The other runtimes have no vector type to subtract, so use the native instead:

```js title="JavaScript"
const away = GetDistanceBetweenCoords(a[0], a[1], a[2], b[0], b[1], b[2], true);
```

## Natives with pointer outputs

Some natives hand values back through pointers. Lua and JavaScript both turn those into extra return values; C# takes them as `ref` parameters, in their real position in the argument list, and a `ref` argument has to be assigned first.

```lua title="Lua"
local ok, value = StatGetInt(stat, -1)
```

```js title="JavaScript"
const [ok, value] = StatGetInt(stat, -1);
```

```csharp title="C#"
int value = 0;
var ok = StatGetInt(stat, ref value, -1);
```

## Printing

`print` is Lua only.

| Lua | JavaScript | C# |
| --- | --- | --- |
| `print(a, b)` | `console.log(a, b)` | `Debug.WriteLine($"{a} {b}")` |

## Commands and events

```lua title="Lua"
RegisterCommand("car", function(source, args, rawCommand)
    -- ...
end, false)

RegisterNetEvent("myEvent")
AddEventHandler("myEvent", function(a, b) end)
```

```js title="JavaScript"
RegisterCommand("car", async (source, args, raw) => {
    // ...
}, false);

onNet("myEvent", (a, b) => {});   // net event, registers and handles in one call
on("myEvent", (a, b) => {});      // local event
```

```csharp title="C#"
public MyScript()
{
    RegisterCommand("car", new Action<int, List<object>, string>(async (source, args, raw) =>
    {
        // ...
    }), false);

    EventHandlers["myEvent"] += new Action<string, bool>(OnMyEvent);
}
```

C# needs no `RegisterNetEvent`: handling a net event registers it.

## The manifest

```lua title="fxmanifest.lua"
fx_version 'cerulean'
game 'gta5'

client_script 'client.lua'
client_script 'client.js'
client_script 'MyResource.Client.net.dll'
```

The loader picks the runtime from the file extension: `.lua`, `.js` and `.net.dll`.

> [!WARNING]
> There is no `.ts` handler. TypeScript has to be compiled to `.js` and the manifest points at the build output. The official typings are the `@citizenfx/client` and `@citizenfx/server` npm packages; add the matching one to `types` in your tsconfig for the side you are writing.

## Where each runtime has no clean equivalent

- **Backtick hashes.** Lua only. Everywhere else it is a `GetHashKey` call at run time, per call.
- **Multiple return values.** Lua returns them loose, JavaScript packs them into an array, C# uses `ref` parameters.
- **`Wait` at the top level.** Lua only. JavaScript needs an async wrapper, C# needs a method.
- **Varargs and table syntax.** A Lua table is an object literal in JavaScript and an anonymous object (`new { ... }`) or a `Dictionary<,>` in C#, depending on whether the keys are members or data.
- **`TriggerClientEvent`.** Lua and JavaScript take the server id as the second argument; C# takes a `Player` first, or drops it entirely to reach everyone.
- **`source`.** A global during the handler in Lua and JavaScript; in C# it is a `[FromSource] Player` parameter instead.

## Sources

Everything on this page is from the official material, not from memory: the [JavaScript runtime](https://docs.fivem.net/docs/scripting-manual/runtimes/javascript/) and [C# runtime](https://docs.fivem.net/docs/scripting-manual/runtimes/csharp/) pages, [Creating your first script in C#](https://docs.fivem.net/docs/scripting-manual/introduction/creating-your-first-script-csharp/), the [resource manifest reference](https://docs.fivem.net/docs/scripting-reference/resource-manifest/), and the shipped `@citizenfx/client` typings, which are where the exact argument lists and return shapes above come from.

## Related

- [Natives: talking to the game](/docs/development/natives)
- [Threads and performance](/docs/development/threads-performance)
- [Client, server and events](/docs/development/client-server-events)
