Tutorial
This tutorial walks you through installing Konpeito and running your first compiled Ruby code.
1. Installation
Prerequisites
| Dependency | Version | Required for |
|---|---|---|
| Ruby | 4.0.1+ | Always (runs the compiler) |
| LLVM | 20 | CRuby native backend, mruby backend |
| Java | 21+ | JVM backend |
| mruby | 3.x or 4.x | mruby backend |
LLVM, Java, and mruby are needed depending on which backend you use. You only need one.
Install Konpeito
gem install konpeito
Install LLVM 20 (for CRuby backend)
macOS:
brew install llvm@20
ln -sf /opt/homebrew/opt/llvm@20/lib/libLLVM-20.dylib /opt/homebrew/lib/
gem install ruby-llvm
Ubuntu / Debian:
sudo apt install llvm-20 clang-20
gem install ruby-llvm
Fedora:
sudo dnf install llvm20 clang20
gem install ruby-llvm
Install Java 21 (for JVM backend)
macOS:
brew install openjdk@21
Ubuntu / Debian:
sudo apt install openjdk-21-jdk
Fedora:
sudo dnf install java-21-openjdk-devel
Verify your environment
konpeito doctor # check CRuby backend
konpeito doctor --target jvm # check JVM backend
You should see green checkmarks for each dependency. Two warnings are expected and can be safely ignored:
- ASM tool: WARNING — The ASM tool (bytecode assembler for the JVM backend) is built automatically the first time you run a JVM build. No action needed.
- Config: WARNING — No
konpeito.tomlfound in the current directory. This is normal when you haven’t created a project yet. You can create one withkonpeito init, or simply pass source files directly tokonpeito build.
2. Hello World
CRuby backend (native extension)
# hello.rb
module Hello
def self.greet(name)
"Hello, #{name}!"
end
end
konpeito build hello.rb # → hello.bundle (macOS) / hello.so (Linux)
Use it from Ruby:
require_relative "hello"
puts Hello.greet("World") # => "Hello, World!"
The compiled extension is a standard CRuby C extension. It loads into any Ruby process just like a gem written in C. Wrapping your code in a module avoids polluting the top-level namespace — this is the recommended pattern for extension libraries.
JVM backend (standalone JAR)
# hello_jvm.rb
puts "Hello from Konpeito!"
konpeito build --target jvm --run hello_jvm.rb
On the very first JVM build, you will see a “Building ASM tool” message. This is a one-time setup.
Expected output:
Compiling hello_jvm.rb (jvm)
Building ASM tool (first-time setup)...
ASM tool ready.
Running: java -jar hello_jvm.jar
Hello from Konpeito!
Finished in 0.9s -> hello_jvm.jar (36 KB)
The generated JAR is standalone — no Ruby installation needed on the target machine.
3. How Konpeito Works
When Konpeito compiles your code, each operation falls into one of two categories:
- Native — The compiler resolved the types and emitted native CPU instructions (e.g. LLVM:
add i64,fadd double,getelementptr) or typed JVM bytecode (e.g.iadd,dadd). No Ruby method dispatch overhead. - Dynamic fallback — The compiler couldn’t determine the type. The call compiles to
rb_funcallv(LLVM) orinvokedynamic(JVM), which runs at the same speed as regular Ruby. The compiler emits a warning so you know where the boundaries are.
Adding RBS type annotations promotes dynamic fallbacks to native dispatch. You can leave them dynamic if you don’t need the speed there — it’s your call.
Gems and runtime dependencies
When your code says require "some_gem", the compiler checks whether the gem’s source is available on the compile-time load path (specified by -I):
- On the load path (
-I) — The gem’s source files are compiled together with your code into a single extension. Method calls between your code and the gem use direct dispatch, monomorphization, and inlining. - Not on the load path — The compiler emits a
rb_require("some_gem")call so CRuby loads the gem at runtime. Your compiled code can still call the gem’s methods, but those calls go throughrb_funcallv(dynamic dispatch). This still works correctly — it’s just not optimized.
In practice, many applications only need to compile their own code. Gems like UI frameworks or database drivers are bottlenecked by rendering or external services, so compiling them provides little benefit. Use -I when you want to maximize optimization across the entire codebase.
4. CRuby Backend: Practical Examples
Pattern 1: Extension Library
Compile performance-critical functions into a native extension and require it from your regular Ruby application.
Step 1: Write the code
# physics.rb
module Physics
def self.distance(x1, y1, x2, y2)
dx = x2 - x1
dy = y2 - y1
dx * dx + dy * dy
end
def self.sum_distances(xs, ys, n)
total = 0.0
i = 0
while i < n - 1
total = total + distance(xs[i], ys[i], xs[i + 1], ys[i + 1])
i = i + 1
end
total
end
end
Step 2: Optionally add RBS for better optimization
Without RBS, HM inference will still resolve types from literals and call sites. But explicit types let the compiler optimize more aggressively.
Option A: Inline RBS (recommended for getting started)
Write type annotations directly in the Ruby source using rbs-inline comments:
# physics.rb
# rbs_inline: enabled
module Physics
#: (Float x1, Float y1, Float x2, Float y2) -> Float
def self.distance(x1, y1, x2, y2)
dx = x2 - x1
dy = y2 - y1
dx * dx + dy * dy
end
#: (Array[Float] xs, Array[Float] ys, Integer n) -> Float
def self.sum_distances(xs, ys, n)
total = 0.0
i = 0
while i < n - 1
total = total + distance(xs[i], ys[i], xs[i + 1], ys[i + 1])
i = i + 1
end
total
end
end
Option B: Separate RBS file
# physics.rbs
module Physics
def self.distance: (Float x1, Float y1, Float x2, Float y2) -> Float
def self.sum_distances: (Array[Float] xs, Array[Float] ys, Integer n) -> Float
end
Step 3: Compile
# Inline RBS (Option A)
konpeito build --inline physics.rb
# Separate RBS file (Option B)
konpeito build physics.rb
Use -v to see what the compiler inferred and where dynamic fallbacks occur:
konpeito build -v physics.rb
Step 4: Use from Ruby
# app.rb — regular Ruby, NOT compiled by Konpeito
require_relative "physics"
xs = Array.new(10000) { rand }
ys = Array.new(10000) { rand }
puts Physics.sum_distances(xs, ys, 10000)
ruby app.rb
What’s native: distance is fully native — dx * dx + dy * dy compiles to fmul double + fadd double instructions. The while loop in sum_distances is a native counter loop.
What’s dynamic: xs[i] calls rb_funcallv on the Ruby Array (because the Array itself is a CRuby object, not a NativeArray). If you need that inner access to be native too, use NativeArray[Float]:
Going fully native with NativeArray
NativeArray[Float] stores unboxed double values in contiguous memory. Array element access becomes a direct getelementptr + load — no method dispatch at all.
Local NativeArray (function-scoped)
Since NativeArray is a Konpeito-specific type, it must be created and accessed within the same compiled scope. Here we put the NativeArray creation, population, and computation together in one method:
# physics_native.rb
# rbs_inline: enabled
module Physics
#: (Float, Float, Float, Float) -> Float
def self.distance(x1, y1, x2, y2)
dx = x2 - x1
dy = y2 - y1
dx * dx + dy * dy
end
def self.run
n = 10000
xs = NativeArray.new(n)
ys = NativeArray.new(n)
i = 0
while i < n
xs[i] = i * 0.0001
ys[i] = i * 0.0002
i = i + 1
end
total = 0.0
i = 0
while i < n - 1
total = total + distance(xs[i], ys[i], xs[i + 1], ys[i + 1])
i = i + 1
end
puts total
end
end
Physics.run
konpeito run physics_native.rb
Tip:
konpeito runcaches compiled artifacts in.konpeito_cache/run/. On subsequent runs, if no source or RBS files have changed, compilation is skipped entirely. Use--no-cacheto force recompilation, or--clean-run-cacheto wipe the cache.
Note: When both
physics_native.rbandphysics_native.bundleexist in the same directory,require "./physics_native"loads the.rbsource file first.konpeito runavoids this problem by handling the load path automatically. If you need to build and run separately, output to a different directory:konpeito build -o build/physics_native.bundle physics_native.rb ruby -r ./build/physics_native -e ""
Important: Local NativeArray values are stack-allocated pointers and cannot be passed as arguments to other methods via CRuby’s method dispatch. Always create and use local NativeArrays within the same function scope. If you need arrays shared across functions, use module NativeArray instead.
Now xs[i] and ys[i] are also native — the entire loop runs without touching Ruby’s method dispatch.
Module NativeArray (global, cross-function)
For game state, simulation data, or any arrays that need to be accessed from multiple functions, you can declare fixed-size NativeArrays as module instance variables in RBS. The compiler generates LLVM global arrays — no C wrapper file needed.
Using inline RBS (rbs_inline: enabled), the module declaration and array types live in the same file:
# physics.rb
# rbs_inline: enabled
# @rbs module PhysicsData
# @rbs @xs: NativeArray[Float, 10000]
# @rbs @ys: NativeArray[Float, 10000]
# @rbs end
#: (Float, Float, Float, Float) -> Float
def distance(x1, y1, x2, y2)
dx = x2 - x1
dy = y2 - y1
dx * dx + dy * dy
end
def init_data
i = 0
while i < 10000
PhysicsData.xs[i] = i * 0.0001
PhysicsData.ys[i] = i * 0.0002
i = i + 1
end
end
#: () -> Float
def compute_total
total = 0.0
i = 0
while i < 9999
total = total + distance(PhysicsData.xs[i], PhysicsData.ys[i],
PhysicsData.xs[i + 1], PhysicsData.ys[i + 1])
i = i + 1
end
total
end
def run_physics
init_data
compute_total
end
konpeito run --inline physics.rb
Or with a separate RBS file:
# physics.rbs
module PhysicsData
@xs: NativeArray[Float, 10000]
@ys: NativeArray[Float, 10000]
end
The syntax is NativeArray[T, N] where T is the element type (Integer or Float) and N is the fixed size. Access uses ModuleName.field_name[index] and ModuleName.field_name[index] = value.
Note: Module NativeArray is available on the LLVM (CRuby) and mruby backends. JVM backend is not yet supported. See
examples/mruby_space_invaders/for a full game using this pattern.
Pattern 2: Whole Application
Compile an entire Ruby application. The compiler traces require / require_relative statements and compiles everything it can find on the load path into a single extension.
Example: A kumiki GUI app
kumiki is a cross-platform desktop UI framework for Ruby.
gem install kumiki
# counter.rb
require "kumiki"
include Kumiki
class CounterComponent < Component
def initialize
super
@count = state(0)
end
def view
column(padding: 16.0, spacing: 8.0) {
text "Count: #{@count}", font_size: 32.0, color: 0xFFC0CAF5, align: :center
row(spacing: 8.0) {
button(" - ") { @count -= 1 }
button(" + ") { @count += 1 }
}
}
end
end
frame = RanmaFrame.new("Kumiki Counter", 400, 300)
app = App.new(frame, CounterComponent.new)
app.run
Option A: Compile everything (whole application)
konpeito build -I /path/to/kumiki/lib counter.rb
-I adds kumiki’s lib/ directory to the load path. The compiler resolves require "kumiki" and all of kumiki’s internal requires, compiling 59 files into a single counter.bundle (971 KB). Method calls between your code and kumiki use direct dispatch; monomorphization and inlining are applied across the entire codebase.
Option B: Compile only your code (extension library)
konpeito build counter.rb
Without -I, the compiler compiles only counter.rb (~50 KB). kumiki is loaded at runtime by CRuby via rb_require. Your code is still compiled natively, but calls into kumiki go through rb_funcallv (dynamic dispatch).
Run:
konpeito run counter.rb
Or build to a separate directory to avoid the .rb / .bundle name conflict:
konpeito build -o build/counter.bundle counter.rb
ruby -r ./build/counter -e ""
-r ./build/counterloads the extension. ItsInitfunction runs the top-level code (creates the frame, component, and enters the event loop).-e ""provides an empty script so Ruby doesn’t wait for stdin.- Do not use
ruby -r ./counter -e ""in the same directory ascounter.rb— Ruby loads.rbbefore.bundle, so it would run the uncompiled source instead.
5. JVM Backend: Practical Examples
Standalone Program
# physics_jvm.rb
def distance(x1, y1, x2, y2)
dx = x2 - x1
dy = y2 - y1
dx * dx + dy * dy
end
def sum_distances(n)
total = 0.0
i = 0
while i < n
total = total + distance(i * 1.0, 0.0, 0.0, i * 2.0)
i = i + 1
end
total
end
puts sum_distances(1000)
konpeito build --target jvm --run physics_jvm.rb
Or produce a JAR and run it separately:
konpeito build --target jvm -o physics.jar physics_jvm.rb
java -jar physics.jar
The JAR is self-contained — no Ruby installation needed on the target machine.
GUI Application (Castella UI)
The JVM backend supports Castella UI, a reactive GUI framework based on Skia rendering.
git clone https://github.com/i2y/konpeito.git
cd konpeito/examples/castella_ui
bash setup.sh # downloads JWM + Skija JARs (~30 MB, one-time)
bash run.sh framework_counter.rb
# framework_counter.rb
class CounterApp < Component
def initialize
super
@count = state(0)
end
def view
label = "Count: " + @count.value.to_s
Column(
Text(label).font_size(32.0),
Row(
Button(" - ").on_click { @count -= 1 },
Button(" + ").on_click { @count += 1 }
).spacing(8.0)
)
end
end
$theme = theme_tokyo_night
frame = JWMFrame.new("Counter", 400, 300)
app = App.new(frame, CounterApp.new)
app.run
See Getting Started for the full Castella UI widget catalog and theme list.
5.5. mruby Backend: Standalone Executables
The mruby backend produces standalone executables — no Ruby or Java required on the target machine. This is ideal for distributing applications, writing games, or deploying to environments without a Ruby installation.
Standalone Hello World
# hello.rb
def main
puts "Hello from Konpeito!"
end
main
konpeito build --target mruby -o hello hello.rb
./hello # => Hello from Konpeito!
Or build and run in one step:
konpeito run --target mruby hello.rb
Game Development with raylib stdlib
Konpeito includes a raylib stdlib for the mruby backend. Reference module Raylib in your code and the compiler auto-detects the RBS/C wrapper — no manual setup needed.
# catch_game.rb
module Raylib
end
def main
screen_w = 600
screen_h = 400
Raylib.init_window(screen_w, screen_h, "Catch Game")
Raylib.set_target_fps(60)
paddle_x = screen_w / 2 - 40
paddle_y = screen_h - 40
paddle_speed = 400
obj_x = Raylib.get_random_value(30, screen_w - 30)
obj_y = 0
obj_speed = 150.0
score = 0
while Raylib.window_should_close == 0
dt = Raylib.get_frame_time
if Raylib.key_down?(Raylib.key_left) != 0
paddle_x = paddle_x - (paddle_speed * dt).to_i
end
if Raylib.key_down?(Raylib.key_right) != 0
paddle_x = paddle_x + (paddle_speed * dt).to_i
end
obj_y = obj_y + (obj_speed * dt).to_i
if obj_y > screen_h
obj_x = Raylib.get_random_value(30, screen_w - 30)
obj_y = 0
end
Raylib.begin_drawing
Raylib.clear_background(Raylib.color_black)
Raylib.draw_rectangle(paddle_x, paddle_y, 80, 14, Raylib.color_skyblue)
Raylib.draw_rectangle(obj_x, obj_y, 16, 16, Raylib.color_gold)
Raylib.draw_text("Score: #{score}", 10, 10, 20, Raylib.color_white)
Raylib.end_drawing
end
Raylib.close_window
end
main
konpeito run --target mruby catch_game.rb
UI Layout with Clay stdlib
Konpeito includes a Clay UI stdlib for building Flexbox-style layouts. Clay computes layout from a tree of containers and outputs draw commands rendered via raylib. Reference module Clay in your code for auto-detection.
# Clay layout: open/close containers, set layout/bg, add text
Clay.begin_layout
Clay.open("root")
Clay.layout(1, 16, 16, 16, 16, 8, 1, 0.0, 1, 0.0, 2, 0) # vertical, grow, center-x
Clay.bg(40.0, 40.0, 60.0, 255.0, 0.0)
Clay.open("card")
Clay.layout(1, 12, 12, 12, 12, 4, 1, 0.0, 0, 0.0, 0, 0) # vertical, grow-w, fit-h
Clay.bg(255.0, 255.0, 255.0, 255.0, 8.0) # white, rounded
Clay.border(200.0, 200.0, 200.0, 255.0, 1, 1, 1, 1, 8.0) # gray border
Clay.text("Hello!", font, 24, 40.0, 40.0, 40.0, 255.0, 0)
Clay.close
Clay.close
Clay.end_layout
Clay.render_raylib # render all commands via raylib
Key concepts:
Clay.open(id)/Clay.close— define nested containersClay.layout(dir, pl, pr, pt, pb, gap, sw_type, sw_val, sh_type, sh_val, ax, ay)— Flexbox layoutClay.bg(r, g, b, a, corner_radius)— background color with rounded cornersClay.border(r, g, b, a, top, right, bottom, left, corner_radius)— borderClay.text(str, font_id, size, r, g, b, a, wrap)— text elementClay.pointer_over(id)/Clay.pointer_over_i(id, index)— hit testing
See examples/mruby_clay_ui/ for a full sidebar layout demo and a Memory Match card game.
Game Framework
Konpeito includes a game framework (game_framework.rb) with helpers for building 2D games:
- Tilemap rendering, sprite animation, scene management, NPC system, text boxes
- Tween/easing (linear, quad, cubic, bounce, elastic), screen shake, scene transitions
- Simple physics (AABB, gravity, friction), particle system, object pool
- FSM (finite state machine), timer system, parallax scrolling
- Grid/tile utilities, debug overlay (FPS, collision rects), gamepad abstraction, save/load
- Clay UI helpers:
fw_clay_rpg_window,fw_clay_bar,fw_clay_num,fw_clay_menu_item - Uses
module GwithNativeArrayglobals for zero-allocation game state
See examples/mruby_dq_rpg/ for a JRPG-style demo and examples/game_showcase/ for a platformer demo showcasing physics, particles, tween, FSM, and parallax.
KUI — Immediate-Mode UI Framework
KUI (Konpeito UI) is a pure Ruby immediate-mode UI DSL. Unlike retained-mode frameworks where you build a persistent widget tree, KUI rebuilds the entire UI every frame from function calls. State lives in your own variables — there are no framework-managed widget objects.
# Every frame: call functions to describe the UI
def draw
vpanel pad: 16, gap: 8 do
label "Count: ", size: 20
label_num $count, size: 20
button "Add" do $count = $count + 1 end
end
end
KUI wraps Clay + Raylib (GUI) or ClayTUI + termbox2 (TUI) behind a unified API. Write your UI once, build for either backend.
Counter App (GUI)
# counter_gui.rb
require "kui_gui"
$count = 0
def draw
vpanel pad: 24, gap: 16 do
header pad: 12 do
label "Counter App", size: 28, r: 255, g: 255, b: 255
end
spacer
cpanel gap: 8 do
label "Current Count:", size: 20
hpanel gap: 4 do
spacer
label_num $count, size: 36, r: 100, g: 200, b: 255
spacer
end
end
spacer
hpanel gap: 12 do
spacer
button " - ", size: 20 do
$count = $count - 1
end
button " Reset ", size: 20 do
$count = 0
end
button " + ", size: 20 do
$count = $count + 1
end
spacer
end
spacer
divider
footer do
label "KUI Framework Demo", size: 14, r: 120, g: 120, b: 140
end
end
return 0
end
def main
kui_init("KUI Counter", 450, 350)
kui_theme_dark
while kui_running == 1
kui_begin_frame
draw
kui_end_frame
end
kui_destroy
return 0
end
main
konpeito build --target mruby -o counter_gui counter_gui.rb
./counter_gui
Switching to TUI
To build the same app for the terminal, change only the require line:
# counter_tui.rb
require "kui_tui"
# ... same draw/main code ...
# Add _kui_update_focus at end of frame for keyboard navigation
konpeito build --target mruby -o counter_tui counter_tui.rb
./counter_tui
KUI Widgets
| Widget | Description |
|---|---|
vpanel(pad:, gap:) { } | Vertical container (GROW width/height) |
hpanel(pad:, gap:) { } | Horizontal container (GROW width, FIT height) |
row(gap:) { } | Horizontal container with GROW height (like Castella’s row) |
cpanel(pad:, gap:) { } | Centered container |
fixed_panel(w, h, pad:) { } | Fixed-size container |
pct_panel(wpct, hpct:, pad:, gap:) { } | Percentage-width container (0-100) |
scroll_panel(pad:, gap:) { } | Scrollable vertical container |
card(pad:, gap:) { } | Surface panel with border |
header(pad:) { } | Top bar with primary color |
footer(pad:) { } | Bottom bar with subtle color |
sidebar(w, pad:, gap:) { } | Fixed-width vertical sidebar |
label(text, size:, r:, g:, b:) | Text label |
label_num(value, size:, r:, g:, b:) | Integer display (no string allocation) |
button(text, size:, kind:, flex:, style:) { } | Clickable button with color variants and flex sizing |
menu_item(text, index, cursor, size:) | Selectable menu item |
spacer | Fills available space |
divider(r:, g:, b:) | Horizontal line |
progress_bar(value, max, w, h, r:, g:, b:) | Progress bar |
Button Kind & Flex
Buttons support semantic color variants and flex-based sizing, similar to Castella’s Style system:
# Kind: semantic color (KUI_KIND_DEFAULT/INFO/SUCCESS/WARNING/DANGER)
button "Save", kind: KUI_KIND_SUCCESS do save_data end
button "Delete", kind: KUI_KIND_DANGER do delete_item end
# Flex: percentage of parent width (use inside row)
row gap: 4 do
button "Wide", flex: 75 do end # 75% width
button "Narrow", flex: 25 do end # 25% width
end
Style Composition
Compose reusable styles with kui_style and kui_style_merge, similar to Castella’s Style.new and +:
# Define base style
btn = kui_style(size: 32, flex: 25)
# Compose variants (non-zero values override)
op = kui_style_merge(btn, kui_style(kind: KUI_KIND_WARNING))
ac = kui_style_merge(btn, kui_style(kind: KUI_KIND_DANGER, flex: 75))
eq = kui_style_merge(btn, kui_style(kind: KUI_KIND_SUCCESS))
# Use with button
row gap: 4 do
button "AC", style: ac do all_clear end
button "/", style: op do press_operator(OP_DIV) end
end
Lifecycle & Theming
kui_init("Title", width, height) # Initialize window/terminal
# 7 theme presets available:
kui_theme_dark # Dark (default)
kui_theme_light # Light
kui_theme_tokyo_night # Tokyo Night (Castella default)
kui_theme_nord # Nord (arctic blue)
kui_theme_dracula # Dracula (vibrant dark)
kui_theme_catppuccin # Catppuccin Mocha (pastel)
kui_theme_material # Material Design (light)
while kui_running == 1
kui_begin_frame
# ... draw UI ...
kui_end_frame
end
kui_destroy # Cleanup
Semantic Background Helpers
Set background color using theme-aware named functions instead of raw KUITheme.c[n]:
card do
kui_bg_surface # theme surface color
kui_bg_surface2 # secondary surface
kui_bg_primary # primary accent
kui_bg_info # informational blue
kui_bg_success # success green
kui_bg_warning # warning orange
kui_bg_danger # danger red
kui_bg_accent # accent color
end
Input
key = kui_key_pressed
# Returns: KUI_KEY_UP, KUI_KEY_DOWN, KUI_KEY_LEFT, KUI_KEY_RIGHT,
# KUI_KEY_ENTER, KUI_KEY_ESC, KUI_KEY_SPACE, KUI_KEY_TAB,
# KUI_KEY_BACKSPACE, or KUI_KEY_NONE
State Management
KUI apps can use Ruby global variables for state:
$score = 0
$player_name = "Alice"
button "Add Point" do
$score = $score + 1
end
For performance-critical apps (games with many state variables), you can use module NativeArray for GC-free state:
# @rbs module G
# @rbs @s: NativeArray[Integer, 64]
# @rbs end
G.s[0] = 42 # write
x = G.s[0] # read
See examples/kui_counter/ for counter demos, examples/kui_dashboard/ for a multi-page dashboard, examples/kui_calc/ for a Castella-style calculator, and examples/kui_cafe/ for a full management simulation game using 20+ widgets.
Shell & File I/O with KonpeitoShell
Execute shell commands and read/write files:
# Auto-detected when KonpeitoShell is referenced
output = KonpeitoShell.exec("ls -la")
status = KonpeitoShell.exec_status # 0 = success
home = KonpeitoShell.getenv("HOME")
KonpeitoShell.setenv("MY_VAR", "value")
content = KonpeitoShell.read_file("data.txt")
KonpeitoShell.write_file("out.txt", "hello")
KonpeitoShell.append_file("log.txt", "new line\n")
if KonpeitoShell.file_exists("config.json") == 1
# ...
end
JSON with KonpeitoJSON
Parse and generate JSON using yyjson:
obj = KonpeitoJSON.parse('{"name": "Alice", "age": 30}')
json = KonpeitoJSON.generate(obj)
HTTP with KonpeitoHTTP
HTTP client using libcurl:
body = KonpeitoHTTP.get("https://example.com")
Cryptography with KonpeitoCrypto
Hashing and encryption using OpenSSL:
hash = KonpeitoCrypto.sha256("hello")
Compression with KonpeitoCompression
Data compression using zlib:
compressed = KonpeitoCompression.gzip("Hello, World!")
decompressed = KonpeitoCompression.gunzip(compressed)
All stdlib modules above are auto-detected — just reference the module name in your code and the compiler will inject the appropriate RBS and C wrappers.
Cross-compilation
Cross-compile for other platforms using zig cc:
konpeito build --target mruby \
--cross aarch64-linux-musl \
--cross-mruby ~/mruby-aarch64 \
-o game game.rb
| Option | Description |
|---|---|
--cross TARGET | Target triple (e.g., x86_64-linux-gnu) |
--cross-mruby DIR | Cross-compiled mruby install path |
--cross-libs DIR | Additional library search path |
mruby vs CRuby backend comparison
| Aspect | CRuby backend | mruby backend |
|---|---|---|
| Output | .so/.bundle (extension) | Standalone executable |
| Runtime dependency | CRuby 4.0+ | None |
| Use case | Library/app acceleration | Distribution, games |
| raylib stdlib | Not available | Auto-detected |
| Clay UI stdlib | Not available | Auto-detected |
| KUI (declarative UI) | Not available | Auto-detected (GUI/TUI) |
| Thread/Mutex | Supported | Not supported |
| Keyword arguments | Supported | Supported |
| Compilation caching | .konpeito_cache/run/ | .konpeito_cache/run/ |
6. Type System
HM type inference (no annotations needed)
Konpeito infers types automatically using Hindley-Milner inference:
def double(x)
x * 2 # 2 is Integer → x is Integer → return is Integer
end
def greet(name)
"Hello, " + name # String + String → String
end
Inferred types are used directly for unboxed optimizations. No RBS needed.
Adding RBS for precision
RBS gives the compiler more precise information:
Separate file:
# sig/math.rbs
module TopLevel
def add: (Integer a, Integer b) -> Integer
end
konpeito build --rbs sig/math.rbs math.rb
Inline (rbs-inline):
# rbs_inline: enabled
#: (Integer, Integer) -> Integer
def add(a, b)
a + b
end
konpeito build --inline math.rb
Native data structures
Typed high-performance data structures are available (CRuby and mruby backends):
| Type | Use case | Characteristics |
|---|---|---|
NativeArray[T] | Numeric arrays | Unboxed, contiguous memory, 5-15x faster |
NativeClass | Structs | Unboxed fields, 10-20x faster |
StaticArray[T, N] | Fixed-size arrays | Stack-allocated, no GC pressure |
NativeHash[K, V] | Hash maps | Linear probing, 4x faster |
Slice[T] | Memory views | Zero-copy, bounds-checked |
# NativeArray example
def sum_array(n)
arr = NativeArray.new(n)
i = 0
while i < n
arr[i] = i * 1.5 # unboxed store
i = i + 1
end
total = 0.0
i = 0
while i < n
total = total + arr[i] # unboxed load
i = i + 1
end
total
end
These types require RBS definitions. See the API Reference for details.
7. Project Setup
konpeito init scaffolds a new project:
konpeito init --target jvm my_app
cd my_app
my_app/
konpeito.toml # build configuration
src/
main.rb # entry point
test/
main_test.rb # test stub
lib/ # JVM dependencies (JARs)
.gitignore
konpeito run src/main.rb # compile & run
konpeito test # run tests
8. Useful Commands
konpeito build -v source.rb # show inferred types and dynamic fallback warnings
konpeito build -g source.rb # emit DWARF debug info for lldb/gdb (LLVM backend)
konpeito check source.rb # type-check only (no code generation)
konpeito build --profile source.rb # build with profiling instrumentation
konpeito run --no-cache source.rb # force recompilation (skip run cache)
konpeito run --clean-run-cache source.rb # clear run cache, then build and run
konpeito fmt # format code (RuboCop)
konpeito deps source.rb # analyze and display dependencies
# mruby backend
konpeito build --target mruby -o app app.rb # standalone executable
konpeito run --target mruby app.rb # build & run (cached)
konpeito doctor --target mruby # check mruby environment
Next Steps
- CLI Reference — All commands and options
- API Reference — Native data structures, standard library, and Castella UI widgets
- Language Specification — Supported syntax and type system rules