Elysium scripting Editor API index
Examples

Cookbook

Short, complete solutions to the jobs scripts are most often written for. Paste, change the numbers, done.

Each recipe is a whole script. They use only what the earlier pages describe, so every call is explained somewhere in the guide.

#Share data between handlers

move runs on the game's thread, once per tick; frame draws once per frame. They can use the same local variables, because only one callback runs at a time. Measure in move, show in frame:

local speed = 0

on( "move", function( cmd )
	local me = ents.local_pawn( )
	speed = me and me:speed2d( ) or 0
end )

on( "frame", function( canvas )
	canvas:text( 20, 20, string.format( "%d u/s", math.floor( speed + 0.5 ) ), color( 255, 255, 255 ), { shadow = true } )
end )

#Do something every few seconds

local count = 0

every( 5, function( )
	count = count + 1
	print( "still running", count )
end )

#Fade something in and out

Keep a value that chases a target, and draw with it. math.approach moves it a bit each frame without overshooting:

local shown   = ui.group( "fade demo" ):toggle( "show", true )
local opacity = 0

on( "frame", function( canvas )
	opacity = math.approach( opacity, shown:get( ) and 1 or 0, render.dt( ) * 4 )

	if opacity > 0 then
		canvas:fill( 20, 20, 160, 40, color( 168, 85, 247 ):fade( opacity ), 8 )
	end
end )

#A key that switches something on

A key control lets the user choose the key and saves it:

local aim_key = ui.group( "assist" ):key( "hold to assist", input.vk.mouse4, "hold" )

on( "frame", function( canvas )
	if aim_key:get( ) then
		canvas:text( 20, 60, "ASSIST", color( 120, 220, 140 ) )
	end
end )

#List the cheat's active hotkeys

on( "frame", function( canvas )
	local y = 240

	for _, bind in ipairs( input.hotkeys( ) ) do
		if bind.active then
			canvas:text( 20, y, bind.name, color( 255, 255, 255 ), { shadow = true } )
			y = y + 16
		end
	end
end )

#Remember something between sessions

local best = store.get( "best_speed", 0 )

on( "move", function( cmd )
	local me = ents.local_pawn( )
	local speed = me and me:speed2d( ) or 0

	if speed > best then
		best = speed
		store.set( "best_speed", best )
	end
end )

on( "frame", function( canvas )
	canvas:text( 20, 20, string.format( "best %d u/s", math.floor( best + 0.5 ) ), color( 255, 255, 255 ) )
end )

#Name tags that follow the players

Project a point above each head with view.project:

on( "frame", function( canvas )
	for _, player in ipairs( ents.players( { enemies = true, alive = true } ) ) do
		local head = player:bone( ents.bone.head )

		if head then
			local x, y, on_screen = view.project( head + vec3( 0, 0, 14 ) )

			if on_screen then
				canvas:text( x, y, player:name( ), color( 255, 255, 255 ), { align = "center", valign = "bottom", shadow = true } )
			end
		end
	end
end )

#A damage log in a window

local window = ui.window{ title = "damage", x = 24, y = 420, w = 260, h = 160, persistent = true, open = true }
local panel  = window:group( "last hits" )
local lines  = { }

on( "game:player_hurt", function( ev )
	local victim, attacker = ev:player( "userid" ), ev:player( "attacker" )

	if attacker and attacker == ents.local_player( ) and victim then
		table.insert( lines, 1, string.format( "%s  -%d", victim:name( ) or "?", ev:int( "dmg_health" ) ) )
		lines[ 7 ] = nil
	end
end )

panel:custom{
	height = 110,

	draw = function( canvas, rect, io )
		for i, line in ipairs( lines ) do
			canvas:text( 4, 2 + ( i - 1 ) * 15, line, color( 255, 255, 255, 255 - i * 25 ) )
		end
	end,
}

#The planted bomb's timer

on( "frame", function( canvas )
	local entity = ents.bomb( )
	local bomb   = entity and entity:bomb( )

	if bomb and not bomb.defused and not bomb.exploded then
		local left = math.max( 0, bomb.blow_time - game.curtime( ) )
		local text = string.format( "%.1f", left )

		canvas:text( 20, 100, "bomb " .. text, left < 10 and color( 244, 63, 94 ) or color( 255, 255, 255 ), { shadow = true } )
	end
end )

#Wallbang damage at the crosshair

Ask the engine what is left of a bullet fired along your view, and print it next to the crosshair:

on( "frame", function( canvas )
	local me = ents.local_pawn( )
	if not me or not me:weapon( ) or not me:weapon( ):is_gun( ) then return end

	local hit = world.bullet( me:eye( ), me:eye_angles( ):forward( ) )

	if hit and hit.surfaces > 0 then
		local width, height = canvas:screen( )
		canvas:text( width / 2 + 14, height / 2 - 6, string.format( "%d (%d)", math.floor( hit.damage + 0.5 ), hit.surfaces ), color( 250, 204, 21 ), { shadow = true } )
	end
end )

#Change a cheat setting for a while

Use a different value while the situation lasts, and put the old one back afterwards - and when the script unloads:

local setting = cheat.find( "ragebot", "doubletap" )
local saved

on( "move", function( cmd )
	local me = ents.local_pawn( )
	if not setting or not me then return end

	if me:on_ground( ) then
		if saved ~= nil then
			setting:set( saved )
			saved = nil
		end
	elseif saved == nil then
		saved = setting:get( )
		setting:set( false )
	end
end )

on( "unload", function( )
	if setting and saved ~= nil then
		setting:set( saved )
	end
end )

#Scroll a long log inside a panel

Clip the text to the panel and move it with the wheel, in a custom control:

local group  = ui.group( "log" )
local lines  = { }
local scroll = 0

for i = 1, 40 do lines[ i ] = "line " .. i end

group:custom{
	height = 140,

	draw = function( canvas, rect, io )
		scroll = math.clamp( scroll - io.scroll * 24, 0, math.max( 0, #lines * 16 - rect.h ) )

		canvas:fill( 0, 0, rect.w, rect.h, color( 0, 0, 0, 90 ), 6 )
		canvas:clip( 0, 0, rect.w, rect.h )

		for i, line in ipairs( lines ) do
			canvas:text( 6, 4 + ( i - 1 ) * 16 - scroll, line, color( 255, 255, 255 ) )
		end

		canvas:unclip( )
	end,
}
Elysium scripting guide · built 2026-10-03