I’m pleased to announce a new release of Kojo. The following are the highlights (for Kojo and the activities around it) since the last announcement:


Functional gaming

Let’s start with a simple animation that runs via the animateWithRedraw command:

cleari()

case class Model(n: Int)

def initState = Model(1)
def update(m: Model) = m.copy(n = m.n + 1)
def view(m: Model) = Picture.text(m.n, 30)

animateWithRedraw(initState, update, view)

With animateWithRedraw, you need to define the following:

  • The state of your animation (i.e. the stuff that changes as your animation runs) – defined here via a Model case class.
  • an init function that returns your initial state.
  • an update function that returns the next state given the previous state.
  • a view function that returns a picture given a state (the picture is the visual representation of the state).

The animation is defined in a purely functional way with the above. To run it and show it on the screen, you call the animateWithRedraw command, passing it the init, update, and view functions.

As the animation runs, it increments a counter in the center of the screen (do you see why?).

Now, what you need (in general) to convert an animation into a game is interactivity, so that messages (like key presses and mouse clicks) from outside can get into your program, enabling you to react to them.

Let’s do a simple cut of this with our animation above. Let’s just make the increasing counter move left/right on key presses, until it moves more than 100 pixels away from the origin:

cleari()
val speed = 5
val limit = 100

case class Model(n: Int, x: Double, y: Double)

trait Msg
case object Tick extends Msg
case object Left extends Msg
case object Right extends Msg
case object Still extends Msg

def initState = Model(1, 0, 0)

def update(m: Model, msg: Msg) = msg match {
    case Tick =>
        m.copy(n = m.n + 1)
    case Left =>
        m.copy(x = m.x - speed)
    case Right =>
        m.copy(x = m.x + speed)
    case Still =>
        m
}

def view(m: Model) = Picture.text(m.n, 30).withTranslation(m.x, m.y)

val tickSub: Sub[Msg] = Subscriptions.onAnimationFrame {
    Tick
}

val keySub: Sub[Msg] = Subscriptions.onKeyDown {
    case Kc.VK_LEFT  => Left
    case Kc.VK_RIGHT => Right
    case _           => Still
}

def subs(m: Model) = {
    if (m.x > -limit && m.x < limit) Seq(tickSub, keySub)
    else Seq()
}

runGame(initState, update, view, subs)
activateCanvas()

Let us see what’s changed compared to the pure animation:

  • You still have a Model case class to represent the state of the game. This contains an x and y now in addition to the counter value.
  • You have a bunch of case classes / objects to represent the possible messages that can come into your game.
  • You still have an init function that returns your initial state.
  • You still have an update function that returns the next state given the previous state. But this function now takes an additional message paramter.
  • You still have a view function as before.
  • You have a new subs function that returns subscriptions for messages that the game is interested in, given the current state of the game. These subscriptions deliver the desired messages into the game.

And there, you have the essentials of a purely functional game!

Based on these ideas, you can see a fuller game here.

Note – Functional gaming in Kojo is a proof of concept prototype (partially based on ideas derived from elm). Many thanks to Anay Kamat for his inputs in this area.


Object-oriented gaming with kojo-gaming

kojo-gaming is a module/extension for Kojo, built with the goal of exposing older students (11th grade, 2nd/3rd year undergrad, and older) to core ideas in practical coding – in an interesting context, with the building of fun (hardware accelerated) games within Kojo that they can export to Desktop and Mobile.

kojo-gaming is based on the object-oriented paradigm (this works very well for gaming in our mind, plus is also widely used in industry), with functional ideas coming in as needed to improve code.

Lunar Lander Game

The core ideas in kojo-gaming are the following:

  • A simple entity component system, with the following features:
    • animated sprites with rendering based on textures, texture atlases, and particle systems.
    • simple physics (kinematics).
    • collision detection and response.
    • world boundary behaviors (bouncing, wrapping, etc).
    • and more…
  • A couple of different implementations:
    • One that works right within the Kojo canvas – for a quick and simple start, and the easy learning of fundamental ideas. This is called game-engine-lite
    • One that is based on libGDX – for putting the fundamental ideas to use in creating fancier games that can be exported to Desktop and Mobile. In this approach, Kojo brings up a new window where all the action happens.

All of this will be described in an upcoming post. For now, to get going, you can just download the game-engine-lite.zip file, unzip it anywhere, and then open/run any of the samples under game-engine-lite/samples. Or you can download the full release of kojo-gaming.


kojo-ai

Kojo-AI examples

kojo-ai is a module/extension for Kojo, built with the goal of providing older students a good learning ground for AI. kojo-ai provides support for things like the following:

  • Learning the essential theoretical concepts in AI.
  • Playing with these concepts within engaging projects.
  • Getting hands-on with the Learning (Training) process of AI models.
  • Building AI-enabled applications.
  • Using pre-trained AI models within scripts.
  • Using Generative AI Apps for design work for games written in Kojo.
  • Gaining insights into generative AI and large-scale foundational models (like ChatGPT).

More on this in an upcoming blog post. For now, to play with kojo-ai, you can use the latest release and provided instructions.


kojo-music

Kojo Music Workbench

kojo-music is a module/extension for Kojo, built with the following basic goals:

  • Provide music as a rich area for children to do creative coding.
  • Enable children to have fun with music as creators and composers - via coding.
  • Enable the usage of Kojo/coding as a new kind of musical instrument.

kojo-music aims to provide an easy start via a Music Workbench, where musical compositions can be created and played, and then exported as code – to enable a gentle entry into coding for music.

kojo-music uses Alda as its music server.

kojo-music will be described in detail in an upcoming blog post. In the meantime, to play with kojo-music you can use the latest release and provided instructions.


Picture clipping and masking

As a quick reminder, Pictures are Kojo’s way of doing functional graphics. To work with Pictures:

  • You create one or more pictures using predefined functions (for various shapes), or by using the turtle-graphics API, or by using lower level primitives from Java2D.
  • You transform these pictures using various predefined transforms like translation, rotation, and scaling.
  • You combine these pictures in rows, columns, and stacks as desired (with rows/columns/stacks of pictures themselves being pictures).
  • And then you finally draw these pictures on the canvas (or use them in animations or games).

Clipping and Masking are a couple of new transforms for Pictures. Let’s get right to them and see them in action.

The following is our base picture (to which clipping and masking will be applied):

cleari()
setBackground(ColorMaker.hsl(0, 0.00, 0.12))

val w = 400
val h = 200
val fill = cm.linearGradient(0, 0, red, 0, h, yellow)
val rect = Picture.rectangle(w, h).withFillColor(fill)

val rectWithReflection = picStack(
    rect,
    rect.withFading(h)
        .withTranslation(0, 3)
        .withFlippedY
)

drawCentered(rectWithReflection)
The base Picture

Let’s now clip the rectangle with a circle (so that only the portion of the rectangle that lies within the circle is visible):

cleari()
setBackground(ColorMaker.hsl(0, 0.0, 0.12))

val w = 400
val h = 200
val fill = cm.linearGradient(0, 0, red, 0, h, yellow)
val rect = Picture.rectangle(w, h).withFillColor(fill)

val rectWithReflection = picStack(
    rect,
    rect.withFading(h)
        .withTranslation(0, 3)
        .withFlippedY
)

val clippingPic = Picture.ellipseInRect(w, h * 2).withTranslation(0, -h)

val clippedPic =
    rectWithReflection
        .withClipping(clippingPic)
        .withPenColor(black)
        .withPenThickness(4)

drawCentered(clippedPic)
The base Picture, the Picture which defines the clip area, and the final clipped Picture.

Finally, let’s apply a mask to the clipped picture. The mask is a black/white (or grayscale) picture, where black specifies transparent regions, and white specifies opaque regions (with gray in between, with darker grays being more transparent):

cleari()
setBackground(ColorMaker.hsl(0, 0.0, 0.12))

val w = 400
val h = 200
val fill = cm.linearGradient(0, 0, red, 0, h, yellow)
val rect = Picture.rectangle(w, h).withFillColor(fill)

val rectWithReflection = picStack(
    rect,
    rect.withFading(h)
        .withTranslation(0, 3)
        .withFlippedY
)

val clippingPic = Picture.ellipseInRect(w, h * 2).withTranslation(0, -h)

val clippedPic =
    rectWithReflection
        .withClipping(clippingPic)
        .withPenColor(black)
        .withPenThickness(4)

val maskPic = picStack(
    Picture.rectangle(w, h * 2).withFillColor(white).withPenColor(white).withPenThickness(4),
    Picture.arc(w / 2, 180).withRotation(90).withScaling(0.9).withTranslation(w+3, h).withFillColor(black).withPenColor(black),
    Picture.arc(w / 2, 180).withRotation(-90).withScaling(0.9).withTranslation(-3, h).withFillColor(black).withPenColor(black)
)

val maskedPic = clippedPic.withMask(maskPic)

drawCentered(maskedPic)
The clipped Picture, The Picture which defines the mask, and the final masked Picture.

Other miscellaneous enhancements

There have been many other enhancements since the last release announcement. Some of these are mentioned below (without a lot of detail for now – we’ll have explanatory blog posts on some of these in the future):

  • Realtime note playing with the setNoteInstrument(instrument) and playNote(note, duration) commands. The Showcase -> Fireworks sample in Kojo has a non-trivial example of this.
  • Using CanvasDraw in animations. CanvasDraw offers a Processing-like api for simple shape drawing, and is useful where you don’t want retained-mode graphics (which is what you get with Pictures). CanvasDraw is used by game-engine-lite in kojo-gaming.
  • Support for drawing text and images in CanvasDraw.
  • Support for spawning external processes from within Kojo. To use this, your script should contain the #exec pragma at the top. This feature is used by kojo-gaming to spawn external libGDX processes.
  • Improved curve drawing.
  • Neural Style Transfer refinements.
  • Apple silicon support.
  • New samples.
  • Turkish enhancements.

That’s it for now…

As always, the new version of Kojo is available from the Kojo Download Page. If you run into any difficulties, let us know.

Enjoy!