Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

Alex ☕🇨🇦

462
Posts
18
Topics
131
Followers
63
Following
A member registered Apr 21, 2018 · View creator page →

Creator of

Recent community posts

I maintain a minimal love2d fennel template. It’s designed to help get you started making games with Love2D and Fennel.

It’s inspired by Phil Hagelberg’s lisp game jam submission exo-encounter-667. The template makes interactive development / hotloading with love2d and fennel painless (or as painless as possible).

This post will walk you through the process of getting started using the template, and how to go about developing games interactively.

Getting started

The first step is to clone the template.

git clone https://codeberg.org/alexjgriffith/min-love2d-fennel.git

You can then start working directly in this cloned directory, just remove the .git folder and init your own.

mv min-love2d-fennel my-new-game
cd my-new-game
rm -rf .git
git init

The latest (or close to the latest) version of fennel will be included in the cloned repository.

The only thing you need to bring to the table is love2d itself. The template has been tested with love2d versions 11.1 through 11.5. It will likely need to be adapted to work with the 12.X pre-release.

You can download love for your system directly from the love2D webpage, or from https://github.com/love2d/love/releases/tag/11.5.

If you’re on linux you can download v 11.5 using wget and move it somewhere on your path.

LOVE_URL=https://github.com/love2d/love/releases/download/11.5/love-11.5-x86_64.AppImage
wget $LOVE_URL -o love
chmod +x love
mv love /somewhere/on/your/path

If you’re using fennel-ls, you can also setup your flsproject file, following the instructions in the Readme.

Customizing the template

There are a few places in the template you should customize before beginning. In the conf.lua you can set the title and identity to be the name of your project. In the makefile you can set your project’s NAME, your name as an AUTHOR, the DESCRIPTION of the project and other identifying information. If you’re planning to upload your games to itch you can also enter your ITCH_ACCOUNT.

Once you have set these values you’re ready to dive into some gamedev!

Template Structure

There are three key files / modules in the template:

  1. main.lua
  2. wrap.fnl
  3. mode-intro.fnl

Love2D’s entry point is always a file called main.lua found at the root of the game directory. The main.lua module in the template initializes fennel and then calls wrap.fnl.

This template is setup to let you write your game as a series of modes. Like a menu mode, a gameplay mode, a pause mode and endgame mode. In practice each mode is just a lua/fennel module.

In wrap.fnl you’ll find the logic for handling modes, and the love2d callbacks. It wraps functions returned by mode modules with these callbacks. Feel free to wrap more functions as you need. Look at love.update as an example.

mode-intro.fnl is an example of what a mode might look like. It has state that is local to the module, it references state that is stored outside the module, and it returns a table of functions that are called in wrap.fnl.

The REPL

When in your project folder run:

love .

You should see a window pop up with a countdown. When it reaches zero the window will close.

You should also see the prompt >> in your shell. This indicates that the REPL is running within the fennel session. You can now interact with the love2d environment just like you would any other fennel environment.

For example, you can copy the following in your shell to stop that dreadful countdown.

(fn love.update [])

While working directly in the REPL is handy for noodling around with a fennel function or exploring the love2d API, it becomes quite cumbersome when working with large functions / groups of functions, which should be grouped together in modules.

The fennel REPL comes with a bunch of built in commands, one of which is the ,reload command.

Try copying the following into the REPL

,reload wrap

You should see that stressful countdown has started again. We’ve reloaded the wrap module, resetting the love.update function we had overwritten to do nothing previously.

Many editors will let you tie commands directly into the repl. For example fennel-mode in Emacs will let you press C-c k to reload a module in a session started using fennel-repl, bound by default to C-c z.

I recommend reading up on other commands that are built into the fennel REPL.

Working with Modes and Hot Reloading

Modifying love2d callbacks like love.draw and love.update interactively can lead to unrecoverable crashes. To avoid this the templates supports using modes instead of directly overwriting love2d callbacks (outside of wrap.fnl). A mode is just a module that returns callbacks like draw and update. You can switch between modes using set-mode.

The update callback takes delta time (dt) as its first argument and the set-mode function as its second. The draw callback takes no arguments. The details on what is passed to other callbacks can be seen in wrap.fnl.

By default wrap.fnl opens mode-intro as its first mode. You can change the mode that is opened in the love.load function, or you can switch to another in an update call using the set-mode function that is passed in.

An absolutely minimum mode may look like this:

;; mode-game.fnl
(fn draw []
  (love.graphics.print "Example Mode!"))
  
{: draw}

It has no update call, and no other callbacks other than draw. It simply writes “Example Mode!” in the top left corner of the screen. Use set-mode to switch to mode-game, either in load.love or the update function in mode-intro.

;; example change update in mode-intro to:
(fn update [dt set-mode] (set-mode :mode-game))

You can swap the print for a printf and center the text by swapping out the draw function with the following:

(fn draw []
  (local screen-width (love.graphics.getWidth))
  (love.graphics.printf "Example Mode!" 0 10 screen-width :center))

Entering this into the REPL will make no change. love.draw is calling the draw function in the mode-game module specifically. Instead try using the ,reload command, either in the command line, or in your editor.

,reload mode-game

You should now see “Example Mode!” printed in the center of the screen!

Errors and Recovery

Not all errors are recoverable. However if there is an error during a draw or update callback, rather then crashing to the love.errorhandler the template will first switch to a mode that will let you try and recover.

Once you’ve identified the error, and reloaded the offending module, you can press space to try and recover. Note sometimes you will have to reload multiple modules in order to recover.

This saves a lot of headaches when hotloading modules with typos or minor logical errors. It also lets you setup watchers that hotload your modules on save with confidence that if you save mid logical thought your game will not crash.

Managing State

There are many ways to manage state in any program or game. This section is just my opinionated position on how to do it in love2d games when you’d like to work intreactivly and hotload modules.

When you require a module it checks to see if that module is already in packages. If it is, rather than re-executing the module it just passes you the table representing the loaded module. Since you’re not re-executing the module file, any local state in the module will be retained once its instantiated.

Hotloading a module throws a wrench in the assumption that module local state is retained. When you hotload a module you’re ejecting the current module from packages and replacing it with a re-executed module file. Any state that was instantiated will be re-instantiated. If you have local variables or tables containing state required by the module stored in the module, and those variables are not defined when the module is first executed you’ll likely crash your program or end up with subtle state bugs.

I’d recommend storing state you want to retain between hotloading in a module dedicated to holding state. In this template that module file is called state.fnl, but you could name it anything you’d like.

My rules of thumb when handling state are:

  1. loading all large resources (like images and music), in the love.load command, or the activate callback of the first module you load. Those resources should be stored in a module that will not be hotloaded at any point.
  2. storing state that needs to be shared between modes or state that needs to be retained between hotloads in a separate module. It should be accessed using require as close to where the state is used as is possible. This module may contain logic that supports resetting the state, for example when you want to restart the game.
  3. keeping parameters that are defined as the module is loaded, or are transient, and are specific to the module as locals at the top level of the module / mode.

Making Your Own Game

With just the callbacks update and draw you can make most simple love2d games without an issue and you can wrap other love2d callbacks as you need. If you’re familiar with lua and have worked with love2d before you could also try out other mode management tools like gamestate. Note, the error mode code works specifically with the mode layout defined in the template. You’ll have to roll your own if you go with another manager.

If you don’t care about hotloading and are just making a game with a simple gameplay mode you could also just write your game directly in the wrap module, eschewing the whole mode concept.

Building your Project

If you’re on linux, mac or have access to wsl you can build and deploy your project using the makefile.

You can make the linux, mac, windows and web builds all in one go using make release or you can build each individually, e.g. make linux.

If you’re uploading to itch you should use butler. Once butler is installed and authorized you can use make upload to upload all of your builds to itch.

Looking in the releases folder you will also find the .love file, which you can manually upload as well.

If you’re building for the web, be warned lovejs has to rely on Lua5.1 rather than luajit, so make sure you write your code in a way that is compatible with both.

In Closing

I always have the most fun developing when I can see the changes I am writing in real time. I hope the minimal love2d fennel template lets you enjoy it just as much.

If you’re looking for inspiration I’ll point you to the blog post that got me started down the interactive game development pipeline: in which a game jam is recounted.

If you’d like even more inspiration check out the numerous games listed in the fennel wiki codebases.

Thanks for the heads up!

Just a couple qs to help debug.

  1. Did it crash when the level loaded, or during gameplay?

  2. What OS are you running? I’ve noticed a few odd crashes on the web port when using safari on macos.

Cool to see clojure running in godot!

Neat fennel lua hybrid file for the input thread module!

Loved that each of the potions was also a little tune!

That wave had me going wtf and jumping out of my seat (I played it at 2am after jamming all weekend)

Thanks for playing! Yeah, I noticed that sometimes they would stall out if no opponents were within aggro range when I was doing my balancing passes. I ended up sticking in that “restart” button during the sim step, but it needs a real solution.

Very visceral, skewmorphic gold!

I love the aesthetic!! Tested out the .love file and no issue on Ubuntu.

Felt very low stress, which was needed after a weekend of Jamming <3

Loved how you guys displayed health and the music was banging.

(1 edit)

The Autumn lisp game jam is almost upon us! Today, its wonderful to see so many lisp dialects and a game frameworks available to use to make games :) This is a far cry from the first jam in 2016.

As always, I am here to evangelize making games with Fennel, a lispy lua, and Love 2d.

I was inspired to start making games with Fennel and Love after reading @technomancy’s 2018 exo encounter devlog, which outlined how to get up and running developing a game interactively with Fennel. If you’d like get started check out the “minimal” love2d fennel template.

The template provides a repl you can use to modify your running environment, and hotload modules that have been modified. Note, careful state management is required when hotloading. Make sure any state you wish to persist is stored outside of the module you are reloading. Feel free to reach out it you have any questions about setting up your game to work interactively.

If you’re just getting started with fennel, but are familiar with lua, the fennel reference is a great place to start. Even if you’re not familiar with lua, (I wasn’t when I started) the language is concise enough to pick up in a weekend! There are also a collection of example codebases to help you get started.

Love 2d is a great framework to get started in. The wiki provides lots of useful examples to get you started, and there are numerous libraries available to help with the game dev process. Some of my favorites are bump, anim8, gamestate and lume.

If you’re brave you can also try targeting the web. The “minimal” love2d fennel template has a “web” target that may be used in the makefile. Note that desktop love 2d relies on luajit. The core of luajit is assembly, which cannot be ported through emscripted to the web. Instead we rely on Lua PUC 5.1, which has some subtle but significant differences in behavior. There are numerous other small gatchas when porting to the web. Make sure you load assets statically rather than streaming them, and avoid the use of threads.

If you’d like to do web first development to avoid fixing these gatchas at the last minute check out the fennel lovejs template. This template has built in support for using both Lua5.1 and Lua5.4 (I’d recommend using Lua 5.4). It lets you both build a single html file that may be hosted and to run a shell/repl so you can interactively modify your running game. It also incorporates a Linux build of Love2d compiled to work with Lua 5.4 that can be used for local testing and for desktop distribution.

Here is a small snippet to show how easy it is to get up and running with love2d and fennel. Happy Jamming!

(var time 0) 

(local display-string "Time since last click: %d")
 
(local font (love.graphics.newFont 30))

(fn love.update [dt]
  (set time (+ time dt)))
  
(fn love.draw []
  (love.graphics.setFont font)
  (love.graphics.print (display-string:format time) 10 10))
  
(fn love.mousepressed [_button _x y]
  (set time 0))

Neat game! Like Frogger and Flappy Bird had a baby, that grew into a chicken.

Thanks for playing! You’ve got to get yourself some wheels for some extra carry capacity. No one survives on foot in the wasteland

Thanks for sharing! What’s your cpu / gpu?

(1 edit)

Thanks for trying it out <3

I’ve got a bug patch ready to go for next week. I’ll look into the issue of people outside of vehicles consuming fuel.

Each person / vehicle has a carry limit for consumables. Getting into a car or truck will let you carry more / use more of the pickups. Outside of a car your avatar can only carry 5 of each.

There was a bug in the web version that prevented you from fixing the vehicles without getting into them first. You should have all the resources you need at your starting outpost to fix up the closest car.

Edit: Found the bug causing fuel consumption it will be addressed in the new patch

Nice little trading sim. Great art and music. Thanks be to the x button!

Nice little racer!

FYI apart from stretching to widescreen it runs great on the lovejs-player The downloaded version is not stretched.

https://alexjgriffith.itch.io/lovejs-player

Simple but fun! Played a bunch of times trying to get all the green plates. Couldn’t quite get them all in one run but came very close.

Ran out of the box no issues on Ubuntu 24.04.

So cute! Loved the vibes

Neat concept! Installation worked well once I had the right python modules installed.

Bingo this did the trick! Thanks <3 Real cool visuals

Neat creation. It’s a very fun little physics engine to play around with.

The controls were very responsive!

Nice to see some hooting in action!

On my system the up down arrows would scroll my screen. My work around was to open the iframe page for the game directly.

https://html-classic.itch.zone/html/13730423/index.html

Great jam game! Intuitive to pick up, and runs very smoothly on the browser.

If you’re going to do a post jam update, I’d love to see the bats speeding up with time and a random heart drop every 25-50 bats killed.

Found the issue! It had to do with how I was building the keys to reference my generated water autotiles :/. Hence why it was showing up when entering water (or when the game restarted and it was trying to reference the player, as if they were on water).

https://alexjgriffith.com/temp/debris-dawn-patch-1.html

Very fun puzzles! The blockers in the later levels were a nice touch to keep the search space small. Very helpful twist!

Thanks for sharing! @mattley found it about 10 min after the jam closed Sunday night.

It’s a Lua5.4 specific error. It handles string casting differently than luajit / lua5.1.

The windows, linux and .love file should all work.

Great aesthetics! Brings me back to my childhood. Nice job getting those shaders working in love and WebGL

Neat demo of goblins in action! Liked watching my crops grow

I’ve put up a build here that limits the width of the intermediate canvases generated to 2048 px. I haven’t made any other changes in this version.

https://alexjgriffith.com/temp/debris-dawn-0.1.0.html

Seems like a reasonable limit. I can definitely wrap the generated image. It’s an easy enough change to make

Thanks for sharing. I generate an intermediate image at start up, which is very wide. I’ll see about turning it into a grid.

Which CPU and GPU are you running?

I’ve got the island generation down, and the constraint based placement of landmarks and objects working. Now time to find out how to make it fun

Island Generation Example

Fennel is a clojure styled lisp that transpiles to Lua. Love2d is one of the easiest game frameworks to get started with.

min-love2d-fennel is all you need to get started writing love2d games with fennel. These days the name is a bit of a misnomer, the template comes with builtin tools to manage game modes, a fennel repl and buildtools required to compile a love2d game for Windows, Mac, Linux and HTML5.

Setup

You can always clone min-love2d-fennel and go from there, but an easier way is to use the new fennel-deps tool by @andreyorst.

Simply clone the fennel-deps repo, and run

deps --new io.gitlab.alexjgriffith/min-love2d-fennel my-awesome-game

To get started with fennel on emacs check out fennel-mode. To make working with love2d and fennel easier check out love2d-fennel.el.

The Basics

If you want to start with the most bare bones setup you can delete everything in the template, apart from main.lua and conf.lua. You can then start playing around with the love2d framework in wrap.fnl. The examples below have been taken from the love2d landing page.

Drawing Text

(fn love.draw []
    (love.graphics.print "Hello World!" 400 300))

Drawing and Image

(local images {})

(fn love.load []
    (tset images :whale (love.graphics.newImage "whale.png")))

(fn love.draw []
    (love.graphics.draw images.whale 300 300)

Playing a Sound

(fn love.load []
    (let [sound (love.audio.newSource :music.ogg :stream)]
    (love.audio.play sound)))

Template Features

The entry point for your fennel code is in wrap.fnl. This module wraps the draw, update and keypressed callbacks of love2d in a way that prevents your project from hard crashing if you make a simple error during development.

Game specific code can be found in the module mode-intro. You can modify mode-intro to suit the needs of your game, or you can write your own modes. wrap.fnl expects mode modules to return a table containing the callbacks activate, draw, update and keypressed.

  • The draw callback takes no arguments.
  • The activate callbacks takes no fixed arguments. Any arguments passed to set-mode are received by this callback.
  • The keypressed callback takes the key pressed and the set-mode function as its arguments.
  • The update callback takes dt (delta time) and the set-mode function as its arguments.

In either keypressed or update you can call set-mode to change the currently running mode.

Example

Below is a simple hello world game made using the template.

Replace mode-intro with mode-game in wrap.fnl.

;; mode-game.fnl
(local fennel (require :lib.fennel))
(fn pp [x] (print (fennel.view x)))

(var message "")

(fn activate []
  (pp "Loading mode :mode-game")
  (set message "Hello World!!")
  ;; load any assets you need for this mode here
  )

(fn draw []
  (love.graphics.print message 300 200))

(fn update [dt set-mode])

(fn keypressed [key set-mode])

{: activate : draw : update : keypressed}

To launch your game either run love . in the command line in the directory with your game or use the command C-c C-z if you are using emacs and you’ve setup fennel-mode and love2d-fennel.el.

Building Your Game

If you want to release your game on Windows, Mac or Linux you can use the provided makefile. Replace the header information in makefile and the titles in conf.lua with the information for your game.

You can make individual builds by running make linux, make mac, make windows and make web. Note for the web we target Lua PUC 5.1 rather than luajit, so if you want your games to run on the web use the subset of lua that is compatable with both Lua PUC 5.1 and luajit.

If you want to release your game on itch.io the makefile also provides the command make release, which will use butler to upload your game to itch.io.

I think you’re right! It should be marquis.

It’s not the first time I misspelled the title! For the initial release I spelt it Marquies 😂

Neat implementation! I also ran out of time trying to get emscripten to play nice with two hosted environments.

Took a couple deaths to figure out what was going on, but it was really engaging once I got the hang of it! Addictive almost…

I am always so impressed with what can get squeezed out of the Tic80!!

This was an absolute blast! The jump and spin physics felt fantastic! Is there a way to recenter the board once it goes out of view?