DevJoy - A package to simplify creation of Visual Novel games.


One time I had a sudden thought.

Actually how hard would it be to write a light novel like game using Elixir? :thinking:

I have played a bit with CSS code and created a dialog message and then I have realized that basically I can render it as component, add more assets like background image, maybe some sounds and it should not be a problem to create a simple game that I can enhance part by part later seeing effects of my work relatively fast and that become my guilty pleasure. :smiling_face:

Before I have realized I wrote few macros and somehow it turn into a fully working hex package. :sweat_smile:

With this package you basically do not have to worry about game logic. You have to simply input data into DSL and start using it by rendering the prepared structs in LiveView or Scenic app - just like you do it for other assigns. :see_no_evil:

What’s best if you use your gettext_module as an option then you have gettext support out of the box (including extracting messages by gettext tasks). :rocket:

Below is a content of the README file. Let me know what do you think about it.


A package to simplify creation of Visual Novel games. License Version Static Badge


def deps do
    {:dev_joy, "~> 1.0"}
  1. Create a new phoenix or scenic project or just use existing one
  2. Add dev_joy to your dependencies
  3. Add English content using DSL
  4. Add assets to your game
  5. Fetch your first scene
  6. Draw your game following output of current_scene_module.get_part() or current_scene_module.get_part(part_name)
  7. Optionally translate content using gettext (call mix gettext.extract).

Except drawing you only have to provide a content to the game. To “draw” your game all you need to do is to convert structs into phoenix templates or scenic graphs and style them respectively.

Example scenes

Main menu

defmodule MyApp.Scenes.Main do
  use DevJoy.Scene

  alias MyApp.Scenes.FirstStage
  alias MyApp.Scenes.SecondStage
  alias MyApp.Scenes.ThirdStage

  part :main do
    menu "Welcome in MyApp!" do
      asset "/assets/main/background1.jpg", size: :stretch_to_fit, type: :backgound
      asset "/assets/main/background1.mp3", loop: true, type: :background
      choice "First stage", goto(FirstStage, :main)
      choice "Second stage", goto(SecondStage, :main)
      choice "Third stage", goto(ThirdStage, :main)
      choice "Settings", goto(:settings)
      choice "Credits", goto(:credits)

  part :credits do
    dialog :admin, "Assets: me (spend lots of time on search for free image and sounds)"
    dialog :admin, "Scenario: me (generated it by AI)"
    dialog :admin, "Translations: me (I'm just young kid, so nobody would notice that I have used some random free translator)"
    dialog :admin, "Game fun: … (on TODO list already!)"
    dialog :admin, "Aren't I the best? >^·^<"

  part :settings do
    dialog :admin, "Still working on it …"
    dialog :admin, "Are you stil here ?!?!"
    dialog :admin, "Oh, please! There are no achievements in this game!"
    dialog :admin, "Seriously …"
    dialog :admin, "Oh, right! I haven't instructed the game to navigate you back to main menu!"

Your game’s first stage

defmodule MyApp.Scenes.FirstStage do
  use DevJoy.Scene

  alias MyApp.Scenes.Main
  alias MyApp.Scenes.SecondStage

  part :main do
    asset "/assets/first-stage/background1.jpg", size: :stretch_to_fit, type: :backgound
    asset "/assets/first-stage/background1.mp3", loop: false, type: :foreground
    dialog :admin, "Lorem ipsum …"
    challenge :input_next_words, count: 3, for: "Lorem ipsum"
    asset "/assets/first-stage/admin-bored.jpg", duration: "0.5s", type: :preview

    question :admin, "Aren't you tired already?" do
      choice "Yes! I'm done!", goto(Main, :main)
      choice "Why? Isn't that just a start?", goto(SecondStage, :main)

I have just released a new version with few bug fixes, page title and chapter support! :rocket:

I’m also working on a full example project based on this package.

Below is a copy of CHANGELOG entry:

1.1.0 (2024-02-28)


  • Added file
  • Added optional and translatable page_title field support for Part struct and its DSL
  • Added t:DevJoy.Gettext.fields_to_translate/0
  • Added DevJoy.Scene.Chapter struct and its DSL

Bug fixes

  • Added missing export option to .formatter.exs file allowing to use locals_without_parens options when importing dev_joy
  • Fixed various :ets related bugs by replacing its usage with GenServer-based solution