# g3d: a small 3D engine that makes LÖVE usable in 3D

> g3d is an MIT licensed Lua 3D engine for the LÖVE 2D framework, adding textured OBJ model rendering, perspective and orthographic cameras, custom shaders, first person controls and collision queries in a single folder you copy into your project. It suits a Lua developer who wants 3D in a LÖVE game without a large engine, and it is not a full game engine with physics, animation or a scene graph.

**groverburger/g3d** — Simple and easy 3D engine for LÖVE.

- Repository: https://github.com/groverburger/g3d
- Stars: 845 · Forks: 42
- Language: Lua
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/groverburger-g3d

## A 3D engine that is one folder in your project

g3d is described as groverburger's 3D engine, and it simplifies LÖVE's 3D capabilities to be as simple to use as possible.

The installation story is three steps and no toolchain. Download the latest release version, add the g3d folder to your LÖVE project, and add a single require line to your main.lua.

```lua
local g3d = require "g3d"
```

There is no build step, no package manager entry, no native library to compile and no configuration file. The repository layout shows why: there is a g3d directory holding the engine, an assets directory, a main.lua that is the demo, a conf.lua which is the standard LÖVE window configuration, and a demo.gif.

That simplicity is the entire design brief, and it is worth understanding what it buys and what it costs. LÖVE is a 2D framework, so there is no 3D support in the engine itself. Writing a 3D renderer inside LÖVE means handling matrices, shaders, depth testing, model transformation and camera maths yourself. g3d does that once so you do not have to.

What you get in exchange is a small scope. The feature list is seven items: textured 3D model rendering, OBJ file loading, perspective and orthographic cameras, custom vertex and fragment shader support, basic first person movement and camera controls, lightweight collision queries, and a claim that the code is simple, commented and organised.

The project is MIT licensed, and the last push to the repository was on 2026-05-03.

## Thirty lines of Lua renders the Earth and Moon demo

The README states that the entire main.lua file for the Earth and Moon demo is under 30 lines, and then shows the whole thing. It is the best single piece of evidence for how the engine is meant to be used.

```lua
local g3d = require "g3d"
local earth = g3d.newModel("assets/sphere.obj", "assets/earth.png", {4,0,0})
local moon = g3d.newModel("assets/sphere.obj", "assets/moon.png", {4,5,0}, nil, 0.5)
local background = g3d.newModel("assets/sphere.obj", "assets/starfield.png", nil, nil, 500)
local timer = 0

function love.update(dt)
    timer = timer + dt
    moon:setTranslation(math.cos(timer)*5 + 4, math.sin(timer)*5, 0)
    moon:setRotation(0, 0, timer - math.pi/2)
    g3d.camera.firstPersonMovement(dt)
    if love.keyboard.isDown "escape" then
        love.event.push "quit"
    end
end

function love.draw()
    earth:draw()
    moon:draw()
    background:draw()
end

function love.mousemoved(x,y, dx,dy)
    g3d.camera.firstPersonLook(dx,dy)
end
```

Three models, three draws, and a per frame update. The structure of a LÖVE game is unchanged, which is the point. There is no scene to declare and no level to load, because the draw order is the order you write the draw calls in.

Look closely at the background model and you see the design of the constructor. The starfield is a sphere with a texture, no position vector, and a scale of 500, which makes it a skybox that follows the camera because it is so large and centred on the origin. The moon passes nil for the same argument, and the earth gets a position of {4,0,0}. That is a three argument constructor where the last two are optional.

The other detail worth noticing is the absence of a scene graph. Models are plain Lua objects with methods, and the update loop calls setTranslation and setRotation on them directly. For a demo that is ideal. For a scene with more than a handful of objects, you will be writing your own bookkeeping.

## newModel takes an OBJ file, a texture and a position

The constructor signature is the API, and reading the three calls in the demo tells you most of what you need.

The first argument is the mesh path, always an OBJ file, and all three demo models share assets/sphere.obj. The second is the texture path, and all three demo models pass one: the earth, the moon and the starfield each get their own PNG. The third is the position, a table of three numbers, and the earth sits at {4,0,0}.

The remaining optional arguments are scale, used for the moon at 0.5, and the background at 500.

So the model of use is: point the engine at a mesh and a texture, and it appears in the world. OBJ is the interchange format, which is the pragmatic choice for a hobby engine, since OBJ is easy to export from almost any modelling tool and easy to read.

The transform methods follow the same plain style. setTranslation takes x, y and z, and setRotation takes three angles, with the demo passing a single timer-derived value for the third. Because the arguments are positional rather than a table, you cannot set only the third rotation axis without passing the others, but for a hobby project that trade is usually fine.

The draw call is simply model:draw(), and the order of the calls in love.draw is the render order. For the demo, the starfield is drawn last, which works because it is the inside of a huge sphere and the depth buffer resolves the rest. This is the kind of thing the wiki covers in more detail, since the README points to it for the camera, models, custom shaders, collisions and other topics.

## First person movement is two helper calls

The camera helpers do the least glamorous and most useful work in the engine.

In the update function, g3d.camera.firstPersonMovement(dt) handles the movement, and in love.mousemoved, g3d.camera.firstPersonLook(dx,dy) handles where the camera is pointing. The delta values from LÖVE's mouse event are passed straight through, so the mouse look is wired to the raw movement rather than to a fixed sensitivity value, which is why the demo can do a first person camera without a single tuning constant.

Everything else in the demo is standard LÖVE. love.keyboard.isDown checks for escape, and love.event.push "quit" ends the program, so the escape key handling is yours and not the engine's.

The camera also supports orthographic projection alongside perspective, which is worth knowing if your project is stylised rather than realistic, since an orthographic camera gives a parallel projection that suits isometric or 2.5D presentation and is much easier to reason about when you are placing things in the scene.

The custom shader support is the other half of the flexibility story. Because the engine allows custom vertex and fragment shaders, you are not locked into its lighting or shading model. The cost is that the wiki is where those examples live, and the README does not show a single shader snippet, so you should plan to read the wiki page on custom shaders rather than expecting to infer the hook points from the code you already have.

## Collision queries are light, and that is the whole scope

One feature in the list is described as lightweight collision queries.

That phrasing is doing a lot of work, and it is the honest word to use. A collision query in this engine means you can ask about the geometry and find out what is there, which is what you need for a first person game where the player must not walk through a wall. It is not a physics simulation.

There is no mention in the feature list of rigid bodies, gravity, friction, joints, or an animation system, and there is no scene graph in the demo. So the realistic description of the engine is a renderer with a camera, a transform API, a first person controller and some geometry queries, and the physics is yours to write or to import.

That is not a criticism. For a hobby 2D framework, a 3D engine with no physics engine is a reasonable scope, because a full physics implementation is a large amount of work to bolt onto something meant to stay small. But it does define the audience. If your game needs objects to fall, stack and settle, g3d will get you to a rendering prototype and no further, and the wiki on collisions is the place to find out exactly what the queries can answer.

The projects listed in the README as made with g3d show the range it has been used for. They include Hoarder's Horrible House of Stuff by alesan99, Lead Haul by YouDoYouBuddy, Plan Meow by SaceMakesGame, a First Person Test by the author, and a separate voxel engine project by the same author. The last one is the most telling, because a voxel engine is a build on top of g3d rather than a game with g3d, and that is the direction the design invites.

## Version 1.6 arrived in December 2025 after a two year gap

The release history has an unusual shape, and it is worth reading before you pin a version.

There are three listed releases. Version 1.6 is dated 2025-12-16. Version 1.5.2 is dated 2022-05-03. Version 1.5.1 is dated 2021-12-23.

So the project spent more than three years at 1.5.2 and then moved to 1.6 in December 2025, with the last push to the repository on 2026-05-03. There are no release notes in the repository listing, so the changelog for 1.6 lives in the release page rather than in a file you can read in place.

For a library that you copy into your project as a folder, this matters more than it would for a package manager dependency. You are vendoring the code, and if you vendor 1.5.2 you are over three years behind, and if you vendor master you are on an untagged commit. The release page is where you find out what changed, so read it before choosing.

The project also has a history you can read before committing. The README links the original forum post on the LÖVE forums for project history and discussion, and the g3d wiki for the reference pages. For a library whose entire user base is people who write games in Lua, a forum thread and a wiki are plausible documentation, and both are worth an hour of your time before you rely on it.

The MIT licence, with the LICENSE file in the repository root, is as close to no friction as a codebase gets, and the games listed in the README are the practical proof that it works in something other than a sphere and a starfield.

## Conclusion

g3d suits a Lua developer who wants to add 3D to a LÖVE project, especially a first person game or a small demo, because the whole engine is one folder and the documented surface is a few constructors and a camera helper rather than a subsystem you have to learn. It does not suit a project that needs rigid body physics, skeletal animation, a scene graph or asset import pipelines, since the feature list covers rendering, cameras, shaders and lightweight collision queries and nothing else. Before you commit, read the wiki pages for the camera, models, custom shaders and collisions, because the README is a summary rather than a reference, take the 1.6 release from December 2025 rather than an older tag, and check the demo assets and main.lua in the repository root, which is the fastest way to see what the engine can actually draw.

## FAQ

### How do I add g3d to a LÖVE project?

Download the latest release version, add the g3d folder to your LÖVE project, and add local g3d = require "g3d" to your main.lua. There is no build step or package manager entry.

### What file formats can g3d load?

The feature list includes OBJ file loading and textured 3D model rendering, and the demo loads assets/sphere.obj with a PNG texture. The wiki covers the models topic in more detail.

### Does g3d support first person controls?

Yes. Basic first person movement and camera controls are listed as a feature, and the demo calls g3d.camera.firstPersonMovement(dt) in love.update and g3d.camera.firstPersonLook(dx,dy) in love.mousemoved.

### Can I use custom shaders with g3d?

The feature list includes custom vertex and fragment shader support, and the g3d wiki has a dedicated section explaining how to use them.

### Which g3d version should I use?

The README says to download the latest release version, and the most recent listed release is version 1.6 dated 2025-12-16, after version 1.5.2 from 2022-05-03. The last push to the repository was on 2026-05-03.

## Sources

- [groverburger/g3d on GitHub](https://github.com/groverburger/g3d)
- [Issues](https://github.com/groverburger/g3d/issues)
- [License: MIT](https://github.com/groverburger/g3d/blob/master/LICENSE)
- [README](https://github.com/groverburger/g3d/blob/master/README.md)
- [Releases](https://github.com/groverburger/g3d/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/groverburger-g3d
