k5-compose is a sketchy port of P5.js for Jetpack Compose Desktop.
This library provides you a playground to play with your sketches so you don't have to worry about maintaining/remembering states and setting up the animations etc. You can focus on creating awesome sketches, creating generative art. This library also provides you necessary physics and math functions which are ported from p5.js.
Say for example you can do something like this in just 20 lines of code -
Moving Vehicle code | K5 Sketch |
---|---|
parametric eq | particles js | gravitation | game of life automaton |
---|---|---|---|
parametric.mov |
particles_js.mov |
gravitation.mov |
game_of_life.mov |
Starfield | Rain drops | Fractal tree | Fireworks |
starfield.mov |
rain.mov |
fractal_tree.mov |
fireworks.mov |
Circle waves | CircleGrid | danceYarn (by @elye_project) | landscapeInspection (by @elye_project) |
circle-waves.mov |
hearts.mov |
dancingYarnMouse.mov |
landscapeInspection.mov |
Also don't forget to check awesome examples and blog posts by few contributors
Click on the link to go to the code. Code explains the things in details. Try playing with those by tweaking values and running on your own. 😄 (I have added videos instead of gifs just so you can view these without loosing any frames 😉)
All the examples can be found here
Blackhole | 10Print | Circle Loop |
---|---|---|
Threads with Perlin-Noise | Phyllotaxis | Mandlebrot (by @elye_project) |
Perlin noise1d (by @elye_project) | Perlin noise2d (by @elye_project) | Ribbon |
In order to understand using this library for creating experiments, I would recommend to go through the Nature of Code book by Daniel Shiffman - https://natureofcode.com/book/.
This will give you the overall knowledge about how a physics system works in simplest way in p5/k5 or in general computer world.
However, you could start digging into it right away by checking examples.
- Create your Jetpack Compose Desktop project. Add the following dependency in your
build.gradle
along with thecompose
dependencies
implementation("me.nikhilchaudhari:k5-compose:{latest-version}")
- Use
k5
dsl construct to create K5 sketch
fun main() = k5{
// Initialise your variables, objects
show { drawScope ->
// use drawScope to draw the shapes
}
}
- Use library apis for calculations and you are good to go! :p
No need to manage/remember states, animation or anything. Just focus on your logic to design sketch rest of the things are handled by the library.
It's very easy, you have to use k5{...}
builder in order to initialise your k5-sketch. The things you define in here will be initialised only once.
fun main() = k5{
// you can define all your properties, variables, vectors here.
val position = Vector2D(20f, 20f)
// Say you want to have a control over your shape and few other parameters like, force, acceleration, mass etc. You can use classes to represent your shapes.
val spaceShip = SpaceShip(position, size) // some data class representing spaceship
//...
}
You can pass size
param in k5()
fun main() = k5(size = Size(800f, 500f)) { ... }
And there are more number of configurations you can do to k5-compose playground which will be applied to the Window{..}
composable. You can check API docs.
Once you have initialised your necessary stuff which is going to change while running the frame of animation, you have to draw your shape/sketch in the show{...}
function. show
function gives you a canvas drawscope which you can used to draw into the k5 compose playground.
Note: Whatever you pass in show{...}
lambda will run for every frame. So if you want to update the values you've initialised and draw the sketch every frame you can pass those things in the lambda. For example -
fun main() = k5{
val vehiclePosition = Vector2D(20f, 20f)
show{ drawScope ->
//update your vehicle position,
vehiclePosition.add(Vector2D.randomVector() * 2f)
drawScope.drawCircle(color = Color.White, radius = 30f, center = vehiclePosition.toOffSet())
}
}
You can apply all the compose Modifiers
to the playground like changing background, color and taking keyboard and mouse pointer input.
show(modifier = Modifier
.background(Color.Yellow)
.pointerMoveFilter(
onMove = {
//use mouse pointer values here
false
}
)
) {
// Draw your sketch
}
If you want to add some interactivity with controls for your sketch, you can use showWithControls(){..}
. You can add readily available Jetpack Compose elements like Slider, Checkbox, Toggle, Button, etc. And use State
to change, update, take the inputs to your k5 sketch.
fun main() = k5 {
val numbers = mutableStateOf(0.3f)
// Here Slider is pure Jetpack Compose element
showWithControls(controls = {
Text("Number of squares")
Slider(
value = numbers.value,
onValueChange = { numbers.value = it }
)
}){ drawScope ->
// Use the numbers value here
// Draw your sketch.
}
Few examples here and here. Which looks something like this - video
This adds 2/3rd of your specified width
to the k5 compose playground window.
noLoop
Here, the vehiclePosition
is constantly updated by adding a random vector to it's previous position and then a new circle is drawn on the playground based on the updated position. Simple, right?
Let's say you don't want to keep your sketch in loop. Let's say you want to draw it only once. You can use noLoop()
method in k5{...}
.
Playground size
You can use dimensInt
or dimensFloat
properties available in k5{...}
to get the size of the playground. You can pass the size
in k5()
as well but there are few density display metrics related issues in using floating point values. So to avoid any miscalculations, these Size
values can be used to get the precise height and width of your playground.
fun main() = k5 {
// Use noLoop to draw your content only once
noLoop()
show {
// this will be drawn only once
}
}
To use and understand mathematics and physics I would recommend Nature Of Code book and Video series by Daniel Shiffman. Or you can go through the examples section in the repo.
Vector2D
is data class - a vector representing any vector quantity in 2D plane. You can create a simple vector like val acceleration = Vector2D(x = 2f, y = 3f)
, this means that the acceleration vector's x component is 2f and y component is 3f.
To perform vector operations there are extensions and operators available to give you all the necessary vector algebra APIs. You can take a look at those methods here in the API Docs.
Few helper methods available to create vectors from angle and also to create random vectors -
Random Vector
You can create random unit vector by using static method randomVector
of Vector2D
class. This creates a random unit vector.
val position = Vector2D.randomVector()
Vector from angle
If you want to create a vector using any angle
you can use fromAngle
static method. For ex - the below code will create a vector with angle as PI radians and length of 2. (means x = 2 * cos(angle), y = 2 * sin(angle))
val position = Vector2D.fromAnAngle(angle = PI, length = 2f)
toOffset
There's another handy method to convert your vector to the Offset
type since you need to pass Offset types while drawing in Jetpack Compose. So you can convert Vector2D into Offset. Using toOffset
on vector value.
val position = Vector2D(2f, 5f)
position.toOffSet()
You can use the Random
functions available in Kotlin by default.
To quickly generate any random number within particular range, there are helper extensions available over ClosedRange
for any Number data types.
For ex - (-12f..-8f).random()
or (1..40).random()
or (1.0..10.0).random()
etc
If you want ot generate randomGaussian
or a random number with your custom set seed
value, apis are available to set the seed for randomness etc. You can check it here. And you can use k5Random
(api doc) and randomGaussian
(api doc) functions to generate random values.
Noise is used a lot in p5 to generate a smooth randomness in numbers. This library contains Perlin noise helper methods which will generate noise values. You can check it here.
There are three methods available for generating noise values - noise1D(x: Double)
, fun noise2D(x: Double, y: Double)
and fun noise3D(x: Double, y: Double, z: Double)
If you don't know what noise is please take a look here
You could of course use the basic kotlin.math trigonometric functions but just to keep it in handy this library has extensions functions over Float
values. So you can just convert any float value to trigonometric function value over this float.
val tan = 1f.tan()
val cos = 0.2f.cos()
val sin = 0.1f.sin()
val atan = 1f.atan()
val acos = 0.2f.acos()
val asin = 0.1f.asin()
You can check few more trigonometric functions here
Angle
The default angle measurement is in radians. If you want to change the angles to be measured in the degrees, then you can set the angleMode
to degrees. And degress will be used to measure the angle and in all the functions related to angles.
angleMode = AngleMode.DEGREES
There are certain calculations that are related to vector, numbers etc which are required when you write physics system in a 2D environment. Those few methods are directly ported from p5.js. You can find some functions like lerp
, map
, norm
, constrain
etc. here
- @elye_project has added awesome animations using perlin noise check them out here
- He has also written a blog post- How to write animations under 50 lines using K5 Compose in Jetpack Compose
PRs are welcomed! Please check contribution guide here
Licensed under Apache License, Version 2.0 here