{"id":14460149,"url":"https://github.com/kool-engine/kool","last_synced_at":"2025-06-19T05:03:03.173Z","repository":{"id":29575629,"uuid":"81503047","full_name":"kool-engine/kool","owner":"kool-engine","description":"An OpenGL / WebGPU engine for Desktop JVM, Android and Javascript written in Kotlin","archived":false,"fork":false,"pushed_at":"2024-12-16T18:43:04.000Z","size":45937,"stargazers_count":319,"open_issues_count":12,"forks_count":22,"subscribers_count":15,"default_branch":"main","last_synced_at":"2024-12-16T19:44:08.070Z","etag":null,"topics":["3d","android","deferred-shading","game-development","kotlin","kotlin-multiplatform","opengl","pbr-shading","physics","vulkan","webgl2","webgpu"],"latest_commit_sha":null,"homepage":"https://fabmax.github.io/kool/kool-js","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kool-engine.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-02-09T22:53:47.000Z","updated_at":"2024-12-16T18:41:48.000Z","dependencies_parsed_at":"2023-10-26T23:33:38.043Z","dependency_job_id":"a9a552fc-831a-4e36-b337-d1447123f263","html_url":"https://github.com/kool-engine/kool","commit_stats":{"total_commits":1313,"total_committers":6,"mean_commits":"218.83333333333334","dds":0.07235338918507239,"last_synced_commit":"3a3924939f335764f702541dc39c8aebb523cc44"},"previous_names":["kool-engine/kool","fabmax/kool"],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kool-engine%2Fkool","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kool-engine%2Fkool/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kool-engine%2Fkool/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kool-engine%2Fkool/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kool-engine","download_url":"https://codeload.github.com/kool-engine/kool/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":231294144,"owners_count":18354140,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["3d","android","deferred-shading","game-development","kotlin","kotlin-multiplatform","opengl","pbr-shading","physics","vulkan","webgl2","webgpu"],"created_at":"2024-09-01T21:01:06.085Z","updated_at":"2024-12-25T23:30:25.596Z","avatar_url":"https://github.com/kool-engine.png","language":"Kotlin","funding_links":[],"categories":["Kotlin"],"sub_categories":[],"readme":"# kool - An OpenGL / WebGPU graphics engine written in Kotlin\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/kool-engine/kool/blob/master/LICENSE)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/de.fabmax.kool/kool-core/badge.svg)](https://central.sonatype.com/artifact/de.fabmax.kool/kool-core)\n![Build](https://github.com/kool-engine/kool/workflows/Build/badge.svg)\n\nA multi-platform OpenGL / WebGPU / ~~Vulkan~~ game engine that works on Desktop Java, Android and browsers.\n\n### Get In Touch\nFeel free to join the [Discord Server](https://discord.gg/GvsJj2Pk3K)!\n\n## Scene Editor\nCheck out the [web editor](https://kool-engine.github.io/live/kool-editor/)! (Chrome recommended).\n\nThe engine used to be a code-only engine, but I recently made some progress in implementing a graphical scene editor.\nThe editor still is in an early state and lacks several rather essential features, but the basics already work quite\nwell. It is fully implemented within the engine itself (including the UI).\n\nTo unleash the full potential of the editor, you should run it locally. You can either download the editor\ndirectly in the web-version by clicking the `Save Project` button in the upper right corner (this also preserves any\nchanges made in the editor) or you can clone the editor template project (see [usage](#usage) below).\n\nMore editor related documentation is available in [the editor docs](https://kool-engine.github.io/docs/editor).\n\n## Web-Demos\nThe following demos run directly in the browser. Chrome is recommended but other browsers might work as well.\nAll demos are implemented in code-only fashion (i.e. without using the editor). The code for all demos is available in\nthe [kool-demo](kool-demo/src/commonMain/kotlin/de/fabmax/kool/demo) subproject.\n\n- [Island](https://kool-engine.github.io/live/demos/?demo=phys-terrain): Height-map based\n  island incl. some wind-affected vegetation + a basic controllable character.\n- [Physics - Ragdoll](https://kool-engine.github.io/live/demos/?demo=phys-ragdoll): Ragdoll physics demo.\n- [Physics - Vehicle](https://kool-engine.github.io/live/demos/?demo=phys-vehicle): A drivable vehicle (W, A, S, D /\n  cursor keys, R to reset) based on the Nvidia PhysX vehicles SDK.\n- [Physics - Joints](https://kool-engine.github.io/live/demos/?demo=phys-joints): Physics demo consisting of a chain\n  running over two gears. Uses a lot of multi shapes and revolute joints.\n- [Physics - Collision](https://kool-engine.github.io/live/demos/?demo=physics): The obligatory collision physics demo with\n  various different shapes.\n- [Embedded UI](https://kool-engine.github.io/live/demos/?demo=ui): Integrated UI framework implemented completely within\n  the engine. Fast, highly customizable and easy-to-use.\n- [Particles](https://kool-engine.github.io/live/demos/?demo=bees): Two teams of bees fighting against each other.\n  Simulation can be toggled between CPU and compute-shader (if available, i.e. on WebGPU).\n- [Fluffy Bunny](https://kool-engine.github.io/live/demos/?demo=shell): Shell-shading based rendering of animated fur\n  (based on this [video](https://www.youtube.com/watch?v=9dr-tRQzij4)).\n- [Creative Coding](https://kool-engine.github.io/live/demos/?demo=creative-coding): A few relatively simple demos\n  showcasing different techniques of generating procedural geometry.\n- [Procedural Geometry](https://kool-engine.github.io/live/demos/?demo=procedural): Small test-case for\n  procedural geometry; all geometry is generated in code (even the roses! Textures are regular images though). Also,\n  some glass shading (shaft of the wine glass, the wine itself looks quite odd when shaded with refractions and is\n  therefore opaque).\n- [glTF Models](https://kool-engine.github.io/live/demos/?demo=gltf): Various demo models loaded from glTF / glb format\n  - Flight Helmet from [glTF sample models](https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/FlightHelmet)\n  - Polly from [Blender](https://github.com/KhronosGroup/glTF-Blender-Exporter/tree/master/polly)\n  - Coffee Cart from [Poly Haven](https://polyhaven.com/a/CoffeeCart_01)\n  - Camera Model also from [Poly Haven](https://polyhaven.com/a/CoffeeCart_01)\n  - A few feature test models also from the [glTF sample model repository](https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0)\n- [Deferred Shading](https://kool-engine.github.io/live/demos/?demo=deferred): Thousands of dynamic\n  light sources, bloom and ambient occlusion.\n- [Screen-space Ambient Occlusion](https://kool-engine.github.io/live/demos/?demo=ao): Roughly based on\n  [this](http://john-chapman-graphics.blogspot.com/2013/01/ssao-tutorial.html) article by John\n  Chapman with slightly optimized sampling (also shamelessly recreated his demo scene).\n- [Screen-space Reflections](https://kool-engine.github.io/live/demos/?demo=ssr): A simple PBR shaded\n  model with screen-space reflections and up to four spot-lights with dynamic shadows.\n- [Physical Based Rendering](https://kool-engine.github.io/live/demos/?demo=pbr): Interactive PBR demo \n  with image based lighting for various materials and environments (underlying PBR theory from\n  [this](https://learnopengl.com/PBR/Theory) awesome article series).\n- [Instanced / LOD Drawing](https://kool-engine.github.io/live/demos/?demo=instance): Instanced rendering\n  demo of the Stanford Bunny. Uses six levels of detail to render up to 8000 instances.\n- [Mesh Simplification](https://kool-engine.github.io/live/demos/?demo=simplification): Interactive mesh\n  simplification demo (based on traditional [error-quadrics](https://www.cs.cmu.edu/~./garland/Papers/quadrics.pdf))\n\nBy default, the web demos use the WebGPU backend and fall back to WebGL if WebGPU is not supported by your browser. The\nused backend is printed in the extended info-panel in the lower right corner (click on the little `+`), apart from\nthat there shouldn't be much visible difference in the WebGL and WebGPU backends. You can also force a certain backend\nby appending `\u0026backend=webgpu` or `\u0026backend=webgl` to the URL.\n\nI also made an actual game with this: [Blocks and Belts](https://fabmaxx.itch.io/blocks-and-belts).\nGive it a try (it's free)!\n\n## Platform Support\n\n| Platform    | Backend     | Implementation Status                                   |\n|-------------|-------------|---------------------------------------------------------|\n| Desktop JVM | OpenGL      | :white_check_mark: Fully working                        |\n| Desktop JVM | Vulkan      | :x: Not working (under construction)                    |\n| Browser     | WebGL 2     | :white_check_mark: Fully working                        |\n| Browser     | WebGPU      | :white_check_mark: Fully working                        |\n| Android     | OpenGL ES 3 | :sparkles: kool-core fully working (but no physics yet) |\n\n### Android Support\n\nThe Android target is disabled by default (to avoid having the Android SDK as a build requirement). You can\nenable the Android target by running the gradle task `enableAndroidPlatform`.\n\nMoreover, Android support is only available for `kool-core` for now. Therefore, the demos don't work on Android yet\n(because they also require `kool-physics`). However, there's a basic [kool-android-template](https://github.com/kool-engine/kool-templates)\nproject with a minimal kool Android app.\n\n## Usage\n\nIf you are adventurous, you can use kool as a library in your own (multiplatform-) projects. The library is published\non maven central, and there is a separate repo containing a minimal template project to get you started:\n\n[https://github.com/kool-engine/kool-templates](https://github.com/kool-engine/kool-templates)\n\nThe demos mentioned above and examples shown below should give you a rough idea on how to do stuff (documentation is\nstill a bit of a weak spot).\n\n### Running the Demos on JVM\n\nYou can launch the desktop demo app directly from a terminal via gradle with `./gradlew :kool-demo:run`. \n\nRunning the [main()](kool-demo/src/desktopMain/kotlin/de/fabmax/kool/demo/Main.kt) method from within IntelliJ\nrequires that the native libraries are located in a local folder and added as file dependencies (seems to be some kind\nof dependency resolution bug in IntelliJ when importing multiplatform projects with JVM runtimeOnly libraries).\n\nThe required libs are copied automatically on build. So, in order to launch the demos from within IntelliJ you need\nto build the project first (or manually run the `cacheRuntimeLibs` task) and then re-sync the gradle project, so that\nthe libs are resolved and added to the IntelliJ module classpath.\n\n## Engine Features / Noticeable Stuff:\n- js: Interchangeable WebGL and WebGPU backends\n- Basic [Compute shader](kool-demo/src/commonMain/kotlin/de/fabmax/kool/demo/helloworld/HelloCompute.kt) support\n- [Reversed-depth](https://developer.nvidia.com/content/depth-precision-visualized) rendering for vastly improved\n  depth precision and range (more or less infinite)\n- Physics simulation (based on Nvidia PhysX 5.3, using [physx-jni](https://github.com/fabmax/physx-jni) on Java and [physx-js-webidl](https://github.com/fabmax/physx-js-webidl) on javascript)\n- Kotlin DSL based shader language (translates into GLSL and WGSL)\n- Neat little integrated GUI framework. The API is heavily inspired by [Jetpack Compose](https://github.com/JetBrains/compose-jb) but the implementation is different, as it needs to run within the OpenGL context.\n- [MSDF](https://github.com/Chlumsky/msdf-atlas-gen) Font support for text rendering in arbitrary font sizes\n- ~~Experimental Vulkan rendering backend (on JVM)~~\n- Support for physical based rendering (with metallic workflow) and image-based lighting\n- (Almost) complete support for [glTF 2.0](https://github.com/KhronosGroup/glTF) model format (including animations, morph targets and skins)\n- Skin / armature mesh animation (vertex shader based)\n- Deferred shading\n- Various tone-mapping options:\n  - ACES (default)\n  - Khronos PBR Neutral\n  - Uncharted 2\n  - Modified Reinhard\n- Screen-space ambient occlusion\n- Normal, roughness, metallic, ambient occlusion\n- Vertex and parallax occlusion displacement mapping\n- Lighting with multiple point, spot and directional lights\n- Shadow mapping for multiple light sources (only spot and directional lights for now)\n- Basic audio support\n\n## A Hello World Example\n\nGetting a basic scene on the screen is quite simple:\n```kotlin\nfun main() = KoolApplication {\n    addScene {\n        defaultOrbitCamera()\n\n        addColorMesh {\n            generate {\n                cube {\n                    colored()\n                }\n            }\n            shader = KslPbrShader {\n                color { vertexColor() }\n                metallic(0f)\n                roughness(0.25f)\n            }\n            onUpdate {\n                transform.rotate(45f.deg * Time.deltaT, Vec3f.X_AXIS)\n            }\n        }\n\n        lighting.singleDirectionalLight {\n            setup(Vec3f(-1f, -1f, -1f))\n            setColor(Color.WHITE, 5f)\n        }\n    }\n}\n```\nThe above example creates an application with a single scene and sets up a mouse-controlled camera\n(with `defaultOrbitCamera()`).\nAs you might have guessed the `addColorMesh { ... }` block creates a colored cube and adds it to the scene.\nIn order to draw the mesh on the screen it needs a shader, which is assigned with\n`shader = KslPbrShader { ... }`. This creates a simple PBR shader for a dielectric material\nwith a rather smooth surface. Color information is taken from the corresponding vertex attribute.\nThe `onUpdate`-block is called on each frame and modifies the cube transform to rotate it 45° per second around its\nX-axis.\nFinally, we set up a single directional scene light (of white color and an intensity of 5), so that our cube can shine\nin its full glory. The resulting scene looks like [this](https://kool-engine.github.io/live/demos/?demo=helloWorld).\n\n## Model Loading and Advanced Lighting\n\nModel loading, animation and more advanced lighting with shadow mapping and ambient occlusion requires only a few more\nlines of code:\n```kotlin\nfun main() = KoolApplication {\n    addScene {\n        defaultOrbitCamera()\n\n        // Light setup\n        lighting.singleSpotLight {\n            setup(Vec3f(5f, 6.25f, 7.5f), Vec3f(-1f, -1.25f, -1.5f), 45f.deg)\n            setColor(Color.WHITE, 300f)\n        }\n        val shadowMap = SimpleShadowMap(this, lighting.lights[0])\n        val aoPipeline = AoPipeline.createForward(this)\n\n        // Add a ground plane\n        addColorMesh {\n            generate {\n                grid { }\n            }\n            shader = KslPbrShader {\n                color { constColor(Color.WHITE) }\n                shadow { addShadowMap(shadowMap) }\n                enableSsao(aoPipeline.aoMap)\n            }\n        }\n\n        // Load a glTF 2.0 model\n        launchOnMainThread {\n            val materialCfg = GltfFile.ModelMaterialConfig(\n                shadowMaps = listOf(shadowMap),\n                scrSpcAmbientOcclusionMap = aoPipeline.aoMap\n            )\n            val modelCfg = GltfFile.ModelGenerateConfig(materialConfig = materialCfg)\n            val model = Assets.loadGltfModel(\"path/to/model.glb\", modelCfg)\n\n            model.transform.translate(0f, 0.5f, 0f)\n            if (model.animations.isNotEmpty()) {\n                model.enableAnimation(0)\n                model.onUpdate {\n                    model.applyAnimation(Time.deltaT)\n                }\n            }\n            \n            // Add loaded model to scene\n            addNode(model)\n        }\n    }\n}\n```\nFirst we set up the lighting. This is very similar to the previous example but this time we use a spot-light, which\nrequires a position, direction and opening angle. Other than directional lights, point and spot-lights have a distinct\n(point-) position and objects are affected less by them, the farther they are away. This usually results in a much\nhigher required light intensity: Here we use an intensity of 300.\n\nNext we create a `SimpleShadowMap` which computes the shadows cast by the light source we defined before.\nMoreover, the created `AoPipeline` computes an ambient occlusion map, which is later used by the shaders to\nfurther improve the visual appearance of the scene.\n\nAfter light setup we can add objects to our scene. First we generate a grid mesh as ground plane. Default size and\nposition of the generated grid are fine, therefore `grid { }` does not need any more configuration. Similar to the\ncolor cube from the previous example, the ground plane uses a PBR shader. However, this time we tell the shader to\nuse the ambient occlusion and shadow maps we created before. Moreover, the shader should not use the vertex color\nattribute, but a simple pre-defined color (white in this case).\n\nFinally, we want to load a glTF 2.0 model. Resources are loaded via the `Assets` object. Since resource loading is a\npotentially long-running operation we do that from within a coroutine launched with `launchOnMainThread { ... }`. \nBy default, the built-in glTF parser creates shaders for all models it loads. The\ncreated shaders can be customized via a provided material configuration, which we use to pass the shadow and\nambient occlusion maps we created during light setup. After we created the custom model / material configuration\nwe can load the model with `Assets.loadGltfModel(\"path/to/model.glb\", modelCfg)`. This suspending function returns the\nloaded model, which can then be customized and inserted into the scene. Here we move the model 0.5 units along the\ny-axis (up). If the model contains any animations, these can be easily activated. This example checks whether there\nare any animations and if so activates the first one. The `model.onUpdate { }` block is executed on every frame and\nupdates the enabled animation. The model is inserted into the scene with `addNode(model)`. Calling `addNode(model)`\nfrom within the coroutine is fine, since the coroutine is launched via `launchOnMainThread { ... }` and therefor\nis executed by the main render thread. If a different coroutine context / thread were used, we had to be careful to\nnot modify the scene content while it is rendered.\n\nThe resulting scene looks like [this](https://kool-engine.github.io/live/demos/?demo=helloGltf). Here, the\n[Animated Box](https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/BoxAnimated) from the glTF sample\nrepository is loaded.\n\n## Kool UI\n\nKool comes with an embedded UI framework, which is heavily inspired by [Jetpack Compose](https://github.com/JetBrains/compose-jb)\nbut was implemented from scratch. Here is a small example:\n```kotlin\nfun main() = KoolApplication {\n    addScene {\n        setupUiScene(Scene.DEFAULT_CLEAR_COLOR)\n      \n        addPanelSurface(colors = Colors.singleColorLight(MdColor.LIGHT_GREEN)) {\n            modifier\n                .size(400.dp, 300.dp)\n                .align(AlignmentX.Center, AlignmentY.Center)\n                .background(RoundRectBackground(colors.background, 16.dp))\n\n            var clickCount by remember(0)\n            Button(\"Click me!\") {\n                modifier\n                    .alignX(AlignmentX.Center)\n                    .margin(sizes.largeGap * 4f)\n                    .padding(horizontal = sizes.largeGap, vertical = sizes.gap)\n                    .font(sizes.largeText)\n                    .onClick { clickCount++ }\n            }\n            Text(\"Button clicked $clickCount times\") {\n                modifier\n                    .alignX(AlignmentX.Center)\n            }\n        }\n    }\n}\n```\nHere, we create a new `Scene` and configure it to be a UI scene containing a single panel surface. Within\nthe `addPanelSurface`-block, we add a button and a text field. All appearance and layout-properties of the UI elements\nare controlled by their `modifier`s.\n\nWhenever the button is clicked we increment a `clickCount` which is then displayed by the text field. This works\nbecause the `addPanelSurfcae`-block is executed each time any `remember`ed state (or `mutableStateOf()`) within the\nblock changes.\n\nThe resulting scene looks like [this](https://kool-engine.github.io/live/demos/?demo=hello-ui).\n\nMore complex layouts can be created by nesting `Row { }` and `Column { }` objects. The\n[kool editor](https://kool-engine.github.io/live/kool-editor/) as well as the\n[full UI demo](https://kool-engine.github.io/live/demos/?demo=ui) should give you an impression on what's possible.\n\n## Kool Shader Language\n\nKool comes with its own shader language (called ksl), which is implemented as a\n[Kotlin Type-safe builder / DSL](https://kotlinlang.org/docs/type-safe-builders.html). The ksl shader code you write is\nused to generate the actual GLSL / WGSL shader code. The benefit with this approach is that there is no hard-coded\nplatform-specific shader code in common code and all shaders work on OpenGL / GLSL as well as WebGPU / WGSL.\nMoreover, it is relatively easy to add different generators which generate shader code for\ndifferent backends in the future (e.g. metal). \n\nWriting shaders in ksl is quite similar to GLSL, here's how a hello-world style shader looks like:\n\n```kotlin\nfun main() = KoolApplication {\n    addScene {\n        defaultOrbitCamera()\n\n        addColorMesh {\n            generate {\n                cube {\n                    colored()\n                }\n            }\n            shader = KslShader(\"Hello world shader\") {\n                val interStageColor = interStageFloat4()\n                vertexStage {\n                    main {\n                        val mvp = mvpMatrix()\n                        val localPosition = float3Var(vertexAttribFloat3(Attribute.POSITIONS))\n                        outPosition set mvp.matrix * float4Value(localPosition, 1f.const)\n                        interStageColor.input set vertexAttribFloat4(Attribute.COLORS)\n                    }\n                }\n                fragmentStage {\n                    main {\n                        colorOutput(interStageColor.output)\n                    }\n                }\n            }\n        }\n    }\n}\n```\nThe interesting part starts at `shader = KslShader() = { ... }`. Here a new shader is created and assigned to the mesh\ncreated before. If you ever wrote a shader before the structure should be familiar: The shader consists of a vertex\nstage (responsible for projecting the individual mesh vertices onto the screen) and a fragment stage (responsible\nfor computing the output-color for each pixel covered by the mesh). This example shader is almost as simple as a valid\nshader can be: It uses a pre-multiplied MVP matrix to project the vertex position attribute to the screen. Moreover,\nthe color attribute is taken from the vertex input and forwarded to the fragment shader via `interStageColor`. The\nfragment stage then simply takes the color from `interStageColor` and writes it to the screen.\n\nA little more complex example is available in [HelloKsl](kool-demo/src/commonMain/kotlin/de/fabmax/kool/demo/helloworld/HelloKsl.kt),\nwhich looks like [this](https://kool-engine.github.io/live/demos/?demo=helloksl).\nOf course, shaders can get more complex than that, you can dig further into the code. All shaders currently used in kool\nare written in ksl.\n\n## Physics Simulation\n\nAfter playing around with various different engines on javascript and JVM I came to the\nconclusion that all of them had some kind of flaw. So I decided to write my own bindings for\n[Nvidia PhysX](https://github.com/NVIDIA-Omniverse/PhysX): [physx-jni](https://github.com/fabmax/physx-jni) for JVM, and\n[physx-js-webidl](https://github.com/fabmax/physx-js-webidl) for javascript.\n\nThis was quite a bit of work, but I think it was worth it: By writing my own bindings\nI get the features I need, and, even better, I get the same features for javascript and JVM, which makes the\nmultiplatform approach much easier.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkool-engine%2Fkool","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkool-engine%2Fkool","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkool-engine%2Fkool/lists"}