Version 1.34
Add modplayer.lua to your sources and import
Load your favorite song
import "modplayer"
Call update every frame to make noise
modplayer.loadSong("game-music.mod")
Control playback
function playdate.update
modplayer.update()
...
end
modplayer.playSong()
...
modplayer.stopSong()
See API reference for more detailed information on how things work.
Playdate MOD player only supports ProTracker .mod modules and its most
common
4-channel extended versions.
Versions
with more than 4 channels or packed modules are not supported.
Here's an exhaustive list of MOD effects and their support status.
| Effect | Support | Description |
|---|---|---|
0xy |
yes | Arpeggio |
1xx |
yes | Portamento up |
2xx |
yes | Portamento down |
3xx |
yes | Tone portamento |
4xy |
yes | Vibrato |
5xy |
yes | Volume slide + Tone portamento. |
6xy |
yes | Volume slide + Vibrato. |
7xy |
yes | Tremolo |
8xx |
yes | Set panning |
9xx |
yes | Sample offset |
Axx |
yes | Volume slide |
Bxx |
yes | Position jump |
Cxx |
yes | Set volume |
Dxx |
yes | Pattern break |
E0x |
-- | Set filter |
E1x |
yes | Fine portamento up |
E2x |
yes | Fine portamento down |
E3x |
-- | Glissando control |
E4x |
-- | Set vibrato waveform. Not supported, always sine |
E5x |
yes | Set finetune. |
E60 |
-- | Pattern loop start |
E6x |
-- | Pattern loop |
E7x |
-- | Set tremolo waveform. Not supported, always sine |
E8x |
yes | Set panning. 8xx is more accurate version of the same effect |
E9x |
yes | Retrigger sample |
EAx |
yes | Fine volume up |
EBx |
yes | Fine volume up |
ECx |
yes | Note cut |
EDx |
yes | Note delay |
EEx |
yes | Pattern delay |
EFx |
-- | Invert loop |
Fxx |
yes | Set speed / tempo |
| Feature | Supported | Description |
|---|---|---|
volume |
yes | Sample specific volume |
finetune |
yes | Pitch finetune |
repeatStart |
yes | Repeat start position |
repeatLength |
yes | Repeat section length (end - start) |
data |
yes | Embedded sample data |
The player is relatively easy on the CPU, but the complexity and speed of the song can affect performance.
Here's data from a minimal test with 5x resampled instruments.

Loading times depend on the used resampling as well as the size of the instrument samples. You can generally expect a song to load in 10-60 seconds when resampling on the device, and 1-3 seconds when using prerendered samples.
While the player can play slower songs at 30 fps or even lower, the timing quality improves with a higher refresh rate. After all, the original PAL Amiga had a refresh rate of 50 fps and NTSC Amiga 60 fps.
Consider running the game at higher framerate than you draw.
Here's a very simple example of how you can achieve this. Please note that in this scenario input still needs to
be polled on every frame.
local frameCounter = 0
function playdate.update()
frameCounter = frameCounter + 1
-- Update song every frame
modplayer.update()
if frameCounter % 2 == 0 then
-- Draw stuff at half the framerate
playdate.graphics.sprite.update()
end
end
coroutine.yield() to prevent the device from
freezing. Playdate will automatically kill your application if it stays frozen for too long.
collectgarbage("incremental")
Playdate MOD player has been designed to not generate any garbage, so you can also turn GC completely off as long
as your own code does not have any memory leaks.
collectgarbage("stop")
Resampling on the device can be slow, so consider using prerendered samples. You can use the provided cacheMod.lua
utility to generate a cache on your PC. Note that you'll need Lua installed on your system.
Here's an example of how to run the tool:
[Windows] lua54 cacheMod.lua 5x songs\song1.mod songs\instruments
[Linux] lua cacheMod.lua 5x songs/song1.mod songs/instruments
[macOS] lua cacheMod.lua 5x songs/song1.mod songs/instruments
After you have the instrument files generated (named 01, 02, ...), configure the player to look for them from the right place. Don't forget to use the same resampling factor so that the player knows how to handle the samples correctly.
modplayer.configureResampling(5, "amiga")
modplayer.configureCacheDirectory("songs/instruments/")
functionmodplayer.loadSong(path,
[progressCallback],
[loadMetadata])
Loads a module from a file.
stringpath
Path to file.
functionprogressCallback(progress)
This function is called multiple times during loading to report progress. You should also
call
coroutine.yield() in the
callback to prevent the device from freezing.
Example:
local function logProgress(progress)
print(tostring(progress * 100.0) .. "%")
coroutine.yield()
end
modplayer.loadSong("song.mod", progressCallback)
booleanloadMetadata
When true, metadata like note names, are also resolved during loading.
functionmodplayer.unloadSong()Unloads the current module from memory.
booleanmodplayer.isPlayingTrue when the song is playing.
booleanmodplayer.readyToPlayTrue when a module has been successfully loaded.
stringmodplayer.versionLibrary version.
functionmodplayer.update()Updates playback. This has to be called once per update.
function playdate.update()
modplayer.update()
end
functionmodplayer.playSong()Starts playing the song. If playback is paused, this will resume playing.
functionmodplayer.getVolume()Gets master volume (0.0 - 1.0).
functionmodplayer.setVolume(volume)Sets master volume.
numbervolume
New master volume (0.0 - 1.0).
functionmodplayer.getChannelMuted()Returns true if the channel is muted.
numberchannel
Channel number (1 - 4).
functionmodplayer.setChannelMuted(volume)Mutes or unmutes a channel.
numberchannel
Channel number (1 - 4).
booleanmute
Mute the channel?
functionmodplayer.setPosition(orderNumber)Set current playback position in pattern table.
integerorderNumber
Pattern order id.
tablemodplayer.playbackInfoPlayback details.
integercurrentPatternCurrent pattern
numberintegercurrentRowCurrent row number
integercurrentOrderCurrent position in
pattern order tabletablemodplayer.metadataSong metadata.
integeridInternal identifier. Can be
used to track song change
stringfilenameFilenamestringnameSong nameintegerchannelCountChannel count
integertempoTempo in BPM.integerspeedSpeed in ticks per
row.integerinstrumentCountInstrument
countintegerpatternCountPattern count
integerorderCountPattern order
table lengthtableorderPattern
order tablefunctionmodplayer.getPattern(patternNumber)Gets a pattern with id.
integerpatternNumber
Pattern id.
functionmodplayer.setTimingMode(mode)Sets playback timing mode.
stringmodeTiming mode
"clock"Advance every 20 ms"crank"Advance only when the crank is rotatedfunctionmodplayer.configureResampling(scale,
interpolation)
Configures load-time resampling. Higher settings result in better sound quality, but considerably increase loading times.
numberscaleSampling rate multiplier
(1x, 2x...). Default value is 1.
stringinterpolationInterpolation
algorithm. Default value is "amiga".
"none"No interpolation"linear"Running average"amiga"Amiga 500 interpolationfunctionmodplayer.configureCacheDirectory(path)Overrides the cache directory. You can use this to include prerendered samples in yout game.
stringpathPath ending with /
Playdate MOD player is licensed under CC0.