Friday Night Funkin' Cookbook
Friday Night Funkin' CookbookIntroductionCustom Songs and Custom Levels

Custom Songs and Custom Levels

Reading time: 7 minutes

This chapter will walk you through the process of creating a functioning, fully compatible Friday Night Funkin' mod, using the game's official systems for loading custom content and scripts. Once your mod is complete, you will be able to place it in the mods folder in your game install and use its content in-game without overriding the base game content and still maintain compatibility with other mods.

This chapter goes over adding new playable songs and Story Mode levels to the game.

Creating a Chart

To create a chart, access the Chart Editor tool. This can be found in-game by accessing the Debug menu from the main menu (this is bound to ~ by default). You can also access it by adding a keybind for "Debug Chart" in the options menu (not bound by default), then pressing the bound key while playing a song.

From here, you can create a new chart from audio files, import one from an older version of the game, or build a chart from existing in-game chart data.

Detailed use of the chart editor deserves its own guide, but the basic should be fairly intuitive. For now, let's assume you've made a chart of your favorite song, and want to turn it into a mod people can play.

Dissecting Your FNFC File

When you save your chart, the game packages it up into a .fnfc file, which makes it easy to share with other charters and collaborate. It includes the audio for the song, along with the note data and some metadata files to go with it.

To add the song to our mod, we need to get that info out. This is fairly easy, because an FNFC file is actually secretly a ZIP file! Rename your mychart.fnfc to mychart.zip, replacing the file extension so that your operating system recognizes it as a ZIP, and extract it to reveal its contents, which will be something like:

-manifest.json
-mychart-metadata.json
-mychart-chart.json
-Inst.ogg
-Voices-bf.ogg
-Voices-pico.ogg

Adding The Custom Song

At the end of Creating A Chart we learned that .fnfc files were just .zip archives, so we can simply rename it and unzip it. Once we have the files, we just need to put them in the correct spots in our mod folder!

  • The manifest.json file can be discarded, our mod won't need it.
  • The metadata.json and chart.json files need to go into the data/songs/<songid> folder, replacing <songid> with the internal name for our song.
  • The OGG files need to go into songs/<songid>, again replacing <songid> with the internal name of our song.

We'll end up with something like this.

-mods
 |-myMod
   |-data
     |-songs
       |-mychart
         |-mychart-metadata.json
         |-mychart-chart.json
   |-songs
     |-mychart
       |-Inst.ogg
       |-Voices-bf.ogg
       |-Voices-pico.ogg
   |-_polymod_meta.json

When the game starts, it queries the list of available songs by looking in the data/songs folder for <songid>/<songid>-metadata.json files, which it then uses to find the chart file and the requisite song files. Neat! But right now, if you boot up the game, this doesn't do anything. You'll see it mentioned in the logs with no complaints, but it's not playable in Story Mode or Freeplay, what gives?

source/funkin/play/song/Song.hx:579: Fetching song metadata for mychart
source/funkin/data/song/SongRegistry.hx:103:   Loaded entry data: Song(mychart)

The fix is simple; every song must be part of a Story Mode level to appear in Freeplay.

Adding a Custom Level

Add a new file, with whatever internal name you like, into your mods folder, under the data/levels/ directory, with the .json file extension.

noteNote

Keep in mind that if your internal name is the same as a week from the base game, or of another mod, they will overlap each other and you may have unexpected results!

We'll end up with something like this.

-mods
 |-myMod
   |-data
     |-levels
       |-myweek.json
     |-songs
       |-mychart
         |-mychart-metadata.json
         |-mychart-chart.json
   |-songs
     |-mychart
       |-Inst.ogg
       |-Voices-bf.ogg
       |-Voices-pico.ogg
   |-images
     |-storymenu
       |-titles
         |-myweek.png
   |-_polymod_meta.json

Your custom week's JSON file will look something like the following:

{
  "version": "1.0.0",
  "name": "MY CUSTOM WEEK",
  "titleAsset": "storymenu/titles/myweek",
  "background": "#F9CF51",
  "songs": ["mychart"],
  "visible": true,
  "props": [
    {
      "assetPath": "storymenu/props/dad",
      "scale": 1.0,
      "offsets": [100, 60],
      "animations": [
        {
          "name": "idle",
          "prefix": "idle0",
          "frameRate": 24
        }
      ]
    },
    {
      "assetPath": "storymenu/props/bf",
      "scale": 1.0,
      "offsets": [150, 80],
      "animations": [
        {
          "name": "idle",
          "prefix": "idle0",
          "frameRate": 24
        },
        {
          "name": "confirm",
          "prefix": "confirm0",
          "frameRate": 24
        }
      ]
    }
  ]
}

There's a lot of info here! Let's break it down:

  • version: The version number for the Level data file format. Leave this at 1.0.0.
  • name: The readable name for the Level, as displayed at the top right of the Story Menu.
  • titleAsset: The asset to use for the level name in the list of level, relative to the images folder in your mod folder.
  • background: The background to use for the level. #F9CF51 is the classic yellow, but this field takes either a color code OR an image file path (relative to the images folder in your mod folder).
  • songs: A list of song IDs to include in the week.
  • visible: Whether this story level is visible in the Story Menu.
  • props: Data for the props to display on the Story Menu when the level is selected. For example, Week 1 will display Daddy Dearest, Boyfriend, and Girlfriend.

When the game starts, it queries the list of available levels by looking in the data/levels folder for JSON files, which it then uses to populate the Story Menu, and then the Freeplay Menu (in non-alphabetical views, songs in Freeplay appear in order by what level they are included in).

If you want your custom song to only show up in Freeplay, you can just create a custom week and set the visible property to false, and the songs will show up in Freeplay!

What Are Variations?

Variations are groups of difficulties for a given song which share metadata.

As an example, the song DadBattle has eight separate difficulties (at time of writing). These are Easy, Normal, Hard, Erect (Erect Mix), Nightmare (Erect Mix), Easy (Pico Mix), Normal (Pico Mix), and Hard (Pico Mix).

These are divided into three variations; Default, Erect, and Pico. Each variation defines information like BPM (and BPM changes), artist, charter, which album the song is from, which stage to use, which character to use in those stages, and which instrumental and vocal track to use. The variation then defines which difficulties it includes, and the chart data for that variation specifies the events for that variation, and the note data and scroll speed for each difficulty.

This means that, in order to make one of these values different for a specific difficulty or remix (including changing the events), you must put that difficulty into a new variation.

The metadata.json file for a song defines what variations exist for that song; the game then looks for metadata-<variationID>.json and chart-<variationID>.json files for each listed variation.

Adding Variations to Existing Songs

Through modding, it is possible to add new variations to existing songs. This is great for adding a new difficulty or remix to an existing song (even if that song is from another mod).

Obtaining Required Files

The first step is to compose a new remix for the song. If you're making a playable character remix, the composer will have to manually make sure the original vocals line up if you want the option to use alternate instrumentals.

You then have to chart this remix. When you're done, you should have an Inst.ogg file, two Voices OGG files, a metadata.json and a chart.json.

Placing the Files

Next, place the assets in the correct spots in our mod folder! Rename each of the files, adding a variation ID of your choice to the end (so if you're making an erect remixes, rename Inst.ogg to Inst-erect.ogg):

-mods
 |-myMod
   |-data
     |-songs
       |-mychart
         |-mychart-metadata-erect.json
         |-mychart-chart-erect.json
   |-songs
     |-mychart
       |-Inst-erect.ogg
       |-Voices-bf-erect.ogg
       |-Voices-pico-erect.ogg
   |-_polymod_meta.json

Registering the Variation in the JSON Data

Each chart includes a songVariations array, which lets the game know which variations the song has available so it can query their respective metadata files. In order to get the game to load your custom variation, you need to modify the metadata.json file for the song's chart, so the game knows that variation exists.

If the song is from your own mod

If the base song you're adding the remix to is from your own mod, you can just add the variation to the metadata.json for your chart.

Add your variation ID to the playData.songVariations array (creating the key if it doesn't exist).

{
  "playData": {
    "songVariations": ["erect"], // Add your new variation to this array.
    // ...
  },
  // ...
}

If the song is from the base game, or a different mod

If the base song you're adding to is from a different mod, you don't want to replace the underlying data in case it changes. You want to instead apply a JSON Patch to the file (which Polymod provides the ability to do).

Create a _merge folder in your mod folder, then create a file within that directory whose path matches the one you want to patch, like so:

-mods
 |-myMod
   |-_merge
     |-data
       |-songs
         |-mychart
           |-mychart-metadata.json

Then we apply a simple patch, which adds a new value to the playData.songVariations array. Edit the JSON file and add these contents:

[
  { "op": "add", "path": "/playData/songVariations/-", "value": "erect" }, // Add a new value erect to the end of the songVariations array.
]

It is possible for the JSON to not contain the playData.songVariations array. In that case, edit the JSON like this instead:

[
  { "op": "add", "path": "/playData/songVariations", "value": ["erect"] }, // Add the 'songVariations' array, and include the value 'erect' with it.
]

The patching system is very flexible; it works on any JSON file (base game or provided by another mod) and has support for advanced behavior.

Conclusion

Now, when you start the game, your additional variation should be available in-game!

If you created a character remix, make sure the player character for the remix is included in the ownedCharacters data for your custom playable character.


Contributors:
doggogit
MightyTheArmiddilo
Pāvels Rimašs
Yas
Cameron Taylor
kade-github
Last modified:
Created:
Category:  Introduction