JavaScript Browser

Source Code

Kotlin compiles to JavaScript just as well as to Java byte code. Therefore, as the final step, we implement our game as an application for web browsers.

The Kotlin Multi Platform Gradle plugin comes with everything we need to create a single page web application. It uses Webpack to generate the necessary JavaScript files and NPM/Yarn to manage the dependencies to JavaScript libraries. All this from within Gradle and with sensible defaults which make the project “just work”.

We start by adding the js target to our common project.

plugins {
    id 'org.jetbrains.kotlin.multiplatform'
}

kotlin {
    jvm()
    js {
        browser()
    }

    sourceSets {

        jsMain {
            dependencies {
                implementation(kotlin("stdlib-js"))
            }
        }
        jsTest {
            dependencies {
                implementation(kotlin("test-js"))
            }
        }

    }
}

As for iOS and Java we now gain the ability to run our tests in JavaScript. This is done under the hood by the Gradle plugin which configures and runs Karma.

JS Testing

As for Java and iOS we need to provide an implementation of a GolTimer.

import kotlin.browser.window

actual class GolTimer actual constructor(action: () -> Unit) {

    private val timer = window.setInterval({ action() }, 500)

    actual fun stop() {
        window.clearInterval(timer)
    }
}

Then as in the previous step we create a new Gradle project. This time a Kotlin JS project and add our common project as a dependency. The Gradle plugin handles the build process automatically.

plugins {
    id 'org.jetbrains.kotlin.js'
}


repositories {
    mavenCentral()
}

dependencies {
    implementation project(':kotlin-mpp-common')
    implementation "org.jetbrains.kotlin:kotlin-stdlib-js"
    testImplementation "org.jetbrains.kotlin:kotlin-test-js"
}

kotlin.target.browser { }

A simple index.html will do the load our application into the browser

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>kotlin-mpp-browser</title>
</head>
<body>

</body>
<script src="kotlin-mpp-browser.js"></script>
</html>

Now we need to program the UI. This can be done in pure Kotlin. All we need is to create an HTML canvas, the button, and provide the necessary callbacks for our existing game logic.

fun main() {

    document.body?.appendElement("button") {
        this as HTMLButtonElement
        textContent = "Pause"
        onclick = {
            if (game.running) {
                textContent = "Resume"
                game.pause()
            } else {
                textContent = "Pause"
                game.resume()
            }
        }
    }

    document.body?.appendElement("canvas") {
        this as HTMLCanvasElement
        width = canvasWidth
        height = canvasHeight
        val context = getContext("2d") as CanvasRenderingContext2D

        fun drawBoard() {
            golCanvas.drawBoard(
                board = board,
                clear = {
                    context.fillStyle = "white"
                    context.fillRect(0.0, 0.0, canvasWidth.toDouble(), canvasHeight.toDouble())
                },
                drawRect = { left, top, size ->
                    context.beginPath()
                    context.fillStyle = "gray"
                    context.fillRect(left.toDouble(), top.toDouble(), size.toDouble(), size.toDouble())
                    context.stroke()
                }
            )
        }

.
        addEventListener("touch", { event: Event ->
            event as TouchEvent
            console.log(event)
        })

        game.afterNextGenerationCalculated = { drawBoard() }
        game.resume()
    }
    
     onwheel = { event ->
                val wheelMax = 100f
                val wheelDelta = event.asDynamic().wheelDelta as Float
                val windowedDelta = when {
                    wheelDelta > wheelMax -> wheelMax
                    wheelDelta < -wheelMax -> -wheelMax
                    else -> wheelDelta
                }
                val zoom = (windowedDelta + wheelMax) / wheelMax
                golCanvas.zoom(board, zoom, event.clientX.toFloat(), event.clientY.toFloat())
                drawBoard()
                event.preventDefault()
            }

}

Use ./gradlew run to start the application. This will compile the Kotlin code to JavaScript, use Webpack to create a single JS file, start a simple webserver and a browser which loads the application.

JavaScript