Naylib manages Raylib resources—such as Image, Wave, Texture, Shader, and Mesh—using Nim’s destructors (RAII) rather than manual Unload* functions.
- No Manual Unloading:
Unload*APIs are intentionally not exported to the user. - Automatic Release: Resources are released automatically when they go out of scope.
- Safety: This approach follows Nim’s memory model to prevent double-free errors and lifetime issues.
If a resource must be released before it goes out of scope—for example, to free up RAM after uploading data to the GPU—use the reset procedure:
var image = loadImage("resources/heightmap.png") # Load heightmap image (RAM)
let texture = loadTextureFromImage(image) # Convert image to texture (VRAM)
reset(image) # Explicitly destroy the RAM resourceThis immediately invokes the destructor. Use this pattern when working with large assets that don't need to persist in system memory.
To maintain strict control over GPU resources, certain types—including Texture, Shader, Mesh, and Font—intentionally do not define =copy hooks. This prevents accidental duplication of handles.
If shared access to a resource is required across different parts of your code, use references:
var texture: ref Texture
new(texture)
texture[] = loadTexture("resources/example.png")
let copy = texture # Copies the reference, not the resource handle itselfNaylib distinguishes between "viewing" a resource and "owning" it, particularly when dealing with 3D models.
Assigning a texture to a model performs only a shallow copy. The model stores the handle but does not take ownership.
Note: The texture must remain valid and in scope for the entire duration of the model’s use.
var model = loadModel("resources/models/plane.obj")
let texture = loadTexture("resources/models/plane_diffuse.png")
model.materials[0].maps[MaterialMapIndex.Diffuse].texture = textureWhen creating a model from a mesh via loadModelFromMesh, ownership is transferred. The sink Mesh parameter consumes the mesh. Because meshes cannot be copied, the compiler enforces that the mesh is moved into the model.
let mesh = genMeshHeightmap(image, Vector3(x: 16, y: 8, z: 16))
var model = loadModelFromMesh(mesh) # Mesh is now owned by the modelAt this point, the original mesh variable should not be used. The model's destructor will eventually unload the mesh.
While most resources are managed automatically, closeWindow requires special care. It must be called at the very end of the program, after all dependent variables have been destroyed.
initWindow(800, 450, "example")
defer: closeWindow()
let texture = loadTexture("resources/example.png")
# Game logic...For larger applications, you can wrap the window lifetime in an object. This ensures the window is the last resource cleaned up by the program.
type Game = object
proc `=destroy`(x: Game) =
assert isWindowReady(), "Window is already closed"
closeWindow()
# Explicitly disable copying and moving for the Game object
proc `=sink`(x: var Game; y: Game) {.error.}
proc `=dup`(y: Game): Game {.error.}
proc `=copy`(x: var Game; y: Game) {.error.}
proc `=wasMoved`(x: var Game) {.error.}
proc initGame(width, height, fps: int32, flags: Flags[ConfigFlags], title: string): Game =
assert not isWindowReady(), "Window is already opened"
setConfigFlags(flags)
initWindow(width, height, title)
setTargetFPS(fps)
let game = initGame(800, 450, 60, flags(Msaa4xHint, WindowHighdpi), "example")When working with data embedded directly in the binary (via exportImageAsCode), use "weak" views to avoid unnecessary memory overhead.
toWeakImage and toWeakWave create non-owning references to static data.
# ImageData is a static array embedded in the binary
let image = toWeakImage(ImageData, ImageWidth, ImageHeight, ImageFormat)
let texture = loadTextureFromImage(Image(image)) You can define custom mappings for external data sources using pixelKind. Naylib will automatically infer the format and validate buffer sizes.
type RGBAPixel* = distinct byte
template pixelKind*(x: typedesc[RGBAPixel]): PixelFormat =
UncompressedR8g8b8a8
let rgba = loadExternalRGBA8(width, height)
let tex = loadTextureFromData(rgba, width, height)
updateTexture(tex, rgba)Naylib is designed to be math-agnostic. If you use external libraries like vmath or glm, you can bridge them using converters.
converter toVector2*(v: geometrymath.Vector2[float32]): raylib.Vector2 {.inline.} =
raylib.Vector2(x: v.x, y: v.y)
converter fromVector2*(v: raylib.Vector2): geometrymath.Vector2[float32] {.inline.} =
geometrymath.Vector2[float32](x: v.x, y: v.y)