-
v0.22.0
Pre-releaseAll checks were successfulreleased this
2026-09-18 00:04:28 +02:00 | 838 commits to main since this releasev0.22.0 — 2026-09-18
Features
-
Every key on the keyboard is bindable — the F-row, Home/End/PageUp/PageDown,
Insert/Delete, Caps Lock and the numpad reach a game as codes 132-166.Until now the platform gave them no code at all, so a game's rebinding screen could
not take one and nothing said why. Windows asked the active layout what they type and
got nothing back (w_vk_charanswers 0 for a key with no character); macOS let them
fall through tocharactersIgnoringModifiers, which reportsNSF1FunctionKeyand its
neighbours at 0xF704 and up - outside the 256-bit held set either way.- The codes are the same on both platforms: 132-143 are F1-F12, 144-149 are Home,
End, PageUp, PageDown, Insert and Delete, 150 is Caps Lock, 152-161 are the numpad
digits and 162-166 its*,+,-,.and/. The numpad's Enter is Enter. Key.F1,Key.Home,Key.Numpad0and the rest fold at compile time like the
other named keys.Input.key_labelnames them - "F1", "Home", "CapsLock", "Num 0", "Num /" - and
does not ask the layout, because a key that types nothing is called the same thing
on every layout.
- The codes are the same on both platforms: 132-143 are F1-F12, 144-149 are Home,
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
ludic-0.22.0-linux-x86_64.tar.gz
0 downloads · 2 MiB
-
ludic-0.22.0-linux-x86_64.tar.gz.sha256
0 downloads · 99 B
-
ludic-0.22.0-src.tar.gz
0 downloads · 3.7 MiB
-
ludic-0.22.0-src.tar.gz.sha256
0 downloads · 90 B
-
-
v0.21.1
Pre-releaseAll checks were successfulreleased this
2026-09-17 15:23:57 +02:00 | 840 commits to main since this releasev0.21.1 — 2026-09-17
Fixes
- A windowed game opens under its package's
app name.ludic build,ludic runand
ludic bundlepass it to the compiler (ludicc --title <name>), sogame_title()- the
window's first title - is the name the player knows rather than theprogramname, which
showed for up to two seconds before the renderer retitled the window. Without anapp name
it is theprogramname, as before. Crypto.random_bytes,Crypto.random_hex,Crypto.random_u32andUuid.*draw from the
system CSPRNG on Windows (RtlGenRandom, advapi32). They read/dev/urandom, which Windows
does not have, and every byte came back zero.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
ludic-0.21.1-linux-x86_64.tar.gz
0 downloads · 2 MiB
-
ludic-0.21.1-linux-x86_64.tar.gz.sha256
0 downloads · 99 B
-
ludic-0.21.1-src.tar.gz
0 downloads · 3.7 MiB
-
ludic-0.21.1-src.tar.gz.sha256
0 downloads · 90 B
- A windowed game opens under its package's
-
v0.21.0
Pre-releaseAll checks were successfulreleased this
2026-09-17 14:59:35 +02:00 | 842 commits to main since this releasev0.21.0 — 2026-09-17
Features
-
A launcher's runtime — start a program, step aside while it runs, and download large
files with a progress bar.Process.*—Process.spawn(path, args)starts a program directly (no shell) with this
process's environment and working directory and returns at once;Process.poll(h)is-1
while it runs, then its exit code (128 + signalfor a signal death,137after
Process.kill);Process.freelets a handle go.posix_spawnon macOS
(runtime/native/process.ll);CreateProcessWon Windows (process_win.ll), with each
argument quoted by the MSVC rules and no console window. Linked only into a program that
usesProcess.*.Http.save_to(h, path)— beforeHttp.send, streams the response body straight into a
file (written atpathitself, created on send) instead of memory;Http.textof such a
request is empty andHttp.body_lenis the bytes written.Http.received(h)and
Http.expected(h)(theContent-Length, or-1) are live while it is pending. macOS
streams through anNSURLSessionwith a delegate built at run time, Windows through the
WinHTTP read loop. Freeing a pending request now cancels it and parks its slot until the
worker has let go, rather than freeing what the worker is still writing.App.window_hide()/App.window_show()— take the game's window off the screen and
bring it back without closing it or its GL / Vulkan surface; the run goes on while it is
hidden. No-ops headless and before there is a window.
Fixes
- A game's window takes the title it asks for. The runtime opens a game's window before
main,
titled with theprogramname, andGl.open/ render3d'sgl_openandgvk_openattached
to it without passing their title on, so aprogram Valleythat opened "Maroon Lake" showed
"Valley".win_set_title(cocoa.llsetTitle:, win32.llSetWindowTextW, UTF-8 to UTF-16)
now retitles it, and a secondwin_openon macOS retitles the open window instead of making
another, as it already did on Windows.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
ludic-0.21.0-linux-x86_64.tar.gz
0 downloads · 2 MiB
-
ludic-0.21.0-linux-x86_64.tar.gz.sha256
0 downloads · 99 B
-
ludic-0.21.0-src.tar.gz
0 downloads · 3.7 MiB
-
ludic-0.21.0-src.tar.gz.sha256
0 downloads · 90 B
-
-
v0.20.0
Pre-releaseAll checks were successfulreleased this
2026-09-17 11:19:19 +02:00 | 846 commits to main since this releasev0.20.0 — 2026-09-17
Features
- The JetBrains plugin (1.6.0) has a Ludic tool window for the package, like the Gradle one:
- an overview of the package (what it is, entry, version, dependencies, scripts, hooks, app);
- its commands, scripts and hooks, run by double-click, with each command's hooks shown;
- its dependencies against package.lock.ludic: the locked version, whether it is fetched
and linked, the indirect ones, and Fetch, Update, Verify, Vendor, Add and Remove; - the app preview the
applines describe, and the asset roots with missing ones marked.
The bar over package.ludic now appears only when something needs doing (fetch, or no
toolchain), instead of carrying every command.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
ludic-0.20.0-linux-x86_64.tar.gz
0 downloads · 1.9 MiB
-
ludic-0.20.0-linux-x86_64.tar.gz.sha256
0 downloads · 99 B
-
ludic-0.20.0-src.tar.gz
0 downloads · 3.7 MiB
-
ludic-0.20.0-src.tar.gz.sha256
0 downloads · 90 B
- The JetBrains plugin (1.6.0) has a Ludic tool window for the package, like the Gradle one:
-
v0.19.0
Pre-releaseAll checks were successfulreleased this
2026-09-17 10:56:30 +02:00 | 848 commits to main since this releasev0.19.0 — 2026-09-17
Features
Udp.*: polled IPv4 datagrams - the transport under a game's own netcode.Udp.open(port)
binds a non-blocking socket (port 0 picks one,Udp.portsays which),Udp.sendsends one
datagram to an address and port, andUdp.recvdrains what arrived without ever blocking;
Udp.from_ip/Udp.from_portname the sender.Udp.ip/Udp.ip_textconvert addresses,
Udp.resolvelooks a host name up andUdp.local_ipis the address a player on the same
network would dial. BSD sockets on macOS (runtime/native/udp.ll), Winsock on Windows
(udp_win.ll, linked with ws2_32), linked only into a program that usesUdp.*.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
ludic-0.19.0-linux-x86_64.tar.gz
0 downloads · 1.9 MiB
-
ludic-0.19.0-linux-x86_64.tar.gz.sha256
0 downloads · 99 B
-
ludic-0.19.0-src.tar.gz
0 downloads · 3.7 MiB
-
ludic-0.19.0-src.tar.gz.sha256
0 downloads · 90 B
-
v0.18.1
Pre-releasereleased this
2026-09-16 15:36:18 +02:00 | 850 commits to main since this releasev0.18.1 — 2026-09-16
Fixes
- The release build links libm on Linux, which
float/doublecode needs (glibc keeps
sinf,sqrtfand the rest there). 0.18.0's release job stopped on this before
publishing, so 0.18.1 is the first published build with float types, barrel imports,
package scripts and hooks, and the JetBrains 1.5.0 support.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
ludic-0.18.1-linux-x86_64.tar.gz
0 downloads · 1.9 MiB
-
ludic-0.18.1-linux-x86_64.tar.gz.sha256
0 downloads · 99 B
-
ludic-0.18.1-src.tar.gz
0 downloads · 3.7 MiB
-
ludic-0.18.1-src.tar.gz.sha256
0 downloads · 90 B
- The release build links libm on Linux, which
-
v0.17.0
Pre-releaseAll checks were successfulreleased this
2026-09-16 15:13:00 +02:00 | 856 commits to main since this releasev0.17.0 — 2026-09-16
Features
- Renderer switches stay out of shipped games — every
R3D_*environment switch in
ludic.render3d(debug views, feature kills, file writers, hardware fakes, the Windows
feature overrides) now goes through one gate: a headless build honours them as before, a
windowed build only whenR3D_DEVis set to anything but0. Seedocs/SHIPPING.md.
Fixes
-
A lake can be the water that mirrors the world — a reflecting body is clipped to its
ellipse like any other unless it is an unbounded sea, so a map whose reflection belongs to
its lake no longer floods every hollow in the survey with that lake's level; and the
terrain's wet shore, forest and scree gates read the carved lake's line inside its outline
(u_lake), the rule the grass already used, so a lake above the sea keeps its wet bank and
no forest is painted down its bed.terrain_lake_carve(false)keeps a lake's line, outline and shore but skips carving its
bed, for a height map that already carries a shaped one.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
ludic-0.17.0-linux-x86_64.tar.gz
0 downloads · 1.7 MiB
-
ludic-0.17.0-linux-x86_64.tar.gz.sha256
0 downloads · 99 B
-
ludic-0.17.0-src.tar.gz
0 downloads · 3.5 MiB
-
ludic-0.17.0-src.tar.gz.sha256
0 downloads · 90 B
- Renderer switches stay out of shipped games — every
-
v0.16.2
Pre-releaseAll checks were successfulreleased this
2026-09-15 22:27:04 +02:00 | 862 commits to main since this releasev0.16.2 — 2026-09-15
Fixes
-
Keys are physical positions on Windows and macOS, whatever the layout -
Input.key_down('w')
is the key above S on every keyboard.- Windows keyed the held set by the character the active layout gave a virtual key, so
a game's WASD belonged to whatever the layout put there: on AZERTY W and A were other
keys, and with an input method on (Chinese, Japanese, Korean) every letter arrived as
VK_PROCESSKEY and no letter key worked at all. The typing block is now read from the
scancode; the arrows, the numpad and the F-keys still go by virtual key, and the frame
key (Input.key, typing) keeps the layout's character. - macOS did the same through
charactersIgnoringModifiers; it now readskeyCode. Input.key_label(key)names a key for the player in their own layout -'w'shows
as "W" on QWERTY and "Z" on AZERTY - so a game that shows its bindings never shows a
key the player cannot find. Windows reads the layout; headless, macOS and the browser
give the US character.
- Windows keyed the held set by the character the active layout gave a virtual key, so
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
ludic-0.16.2-linux-x86_64.tar.gz
0 downloads · 1.7 MiB
-
ludic-0.16.2-linux-x86_64.tar.gz.sha256
0 downloads · 99 B
-
ludic-0.16.2-src.tar.gz
0 downloads · 3.5 MiB
-
ludic-0.16.2-src.tar.gz.sha256
0 downloads · 90 B
-
-
v0.16.1
Pre-releasereleased this
2026-09-15 22:01:01 +02:00 | 865 commits to main since this releasev0.16.1 — 2026-09-15
Features
-
HDR calibration, and two HDR fixes — the HDR10 picture follows the player's display instead of one fixed curve.
r3d_hdr_calibrate(peak, paper, black)— nits as float bits: the brightest the display shows (100-10000),
where the picture's and the interface's white sit (80-1000, never above the peak) and how far the darkest shade is
lifted (0-5, fading out by paper white). The tonemap's and the overlay's HDR10 variants read them, and the display's
HDR metadata is sent again with the new peak. The defaults are the old constants: 1000, 200 and 0.ov_hdr_nits(nits)/ov_hdr_paper()— what the overlay draws next is at that many nits on an HDR10 frame,
for a calibration screen's test patches; it closes the batch so far. No effect on an SDR frame.- Fixed: a crash toggling HDR on the Vulkan renderer.
gpu_caps_probe()made and destroyed a second Vulkan
instance under NVIDIA Streamline's interposer; the next swapchain rebuild then called through a pointer that
instance had left behind, at address 0. With the Vulkan renderer running, the probe asks its instance instead. - Fixed: yellow read as red in HDR. The overlay drew sRGB values straight into the HDR10 swapchain (it now has an
HDR10 variant, chosen whilegpu_hdr_active()), and the tonemap extended highlights per channel, which boosted a
bright yellow's red far more than its green; it now brightens the graded colour by one factor.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
ludic-0.16.1-linux-x86_64.tar.gz
0 downloads · 1.7 MiB
-
ludic-0.16.1-linux-x86_64.tar.gz.sha256
0 downloads · 99 B
-
ludic-0.16.1-src.tar.gz
0 downloads · 3.5 MiB
-
ludic-0.16.1-src.tar.gz.sha256
0 downloads · 90 B
-
-
v0.16.0
Pre-releaseSome checks failedreleased this
2026-09-15 19:43:08 +02:00 | 868 commits to main since this releasev0.16.0 — 2026-09-15
Features
-
A loading screen can show the renderer's start-up as it happens.
r3d_open(w, h, title)opens
the window and the graphics backend with nothing baked, andr3d_load_count()/r3d_load_step(i)
run the rest - the sky and its light, the terrain, the shadow and screen targets, the scattered cover
and actors, the grass - one step at a time, so a game can draw and present a frame between them.
r3d_initis those same calls in a row and is unchanged for everything that uses it. -
HDR10 output on the Vulkan renderer —
r3d_hdr(on)asks for an ST 2084 / BT.2020 swapchain where the
display offers one.The tonemap's HDR10 variant keeps the SDR picture up to a 200-nit paper white and rolls highlights on to
1000 nits; the overlay converts the interface to the same white; HDR metadata is set where the loader has
the command. The screen and LDR images are 10-bit while it is on, and screenshots refuse rather than write
a PQ image as SDR. OpenGL and the Vulkan SDR frame are unchanged.R3D_HDRoverrides the setting. -
Mesh-shader grass — on a card with
VK_EXT_mesh_shader,r3d_mesh_grass(on)draws each grass tile as one
mesh-shader dispatch sized to that tile's blades, and a blade that is culled emits no vertices at all.ludic-dev shaders— a variant whose first file is*.meshcompiles that stage as a Vulkan 1.3 mesh
shader; its SPIR-V keeps the.vertname, so the manifest is unchanged.- The seam —
gpu_has_mesh()andgpu_draw_mesh_tasks(x, y, z); a mesh program's pipeline has no
vertex input and its bindings the mesh stage. The device enablesmeshShaderandmaintenance4.
Not yet faster: at 4K on an RTX 3070 Ti the grass pass takes 5.0 ms against the chunked path's 1.7, so
gpu_feature_implemented(GF_MESH_GRASS)stays false and a game should not offer it as a finished setting. -
More of the renderer's quality is a switch a game can offer.
sky_set_quality(width): the prefiltered sky light at 256, 512 or 1024 wide (half as tall),
baked again at once.post_set_msaa(samples): 1, 2 or 4 samples for the scene on OpenGL, remaking the screen targets in
place (post_msaa_live()is false on Vulkan, which has no sample counts yet);post_freenow
frees the multisampled framebuffer too.STREAM_BUDGET_USis a variable: the microseconds a frame may spend generating streamed cover.r3d_fog_scale: a multiplier over the fog density the daylight and the weather set.
-
NVIDIA DLSS super resolution and Reflex on the Vulkan renderer — through NVIDIA Streamline 2.14.1.
- The loader — on Windows
Vk.open()takessl.interposer.dllbeside the executable in place of
vulkan-1.dllwhen a program asks (Vk.sl_prefer), falling back to plain Vulkan without it; thesl*
exports and feature functions are reachable from Ludic. - render3d —
r3d_dlss(mode)upscales the lit HDR frame before bloom and the tonemap, jittered on a
Halton cycle, with preset K in every mode (the default M cost 18 ms a frame at 4K on an RTX 3070 Ti);
r3d_dlss_render_w/hgive the size to render at.r3d_reflex(mode)sleeps each frame and marks
simulation, submit and present.R3D_DLSS,R3D_REFLEX,R3D_DLSS_PRESET,R3D_SL_LOGfor tests. - Commands the interposer lacks — it exports no
vkSetHdrMetadataEXT,vkCmdDrawMeshTasksEXTor
acceleration-structure command;Vk.haschecks one andvkGetDeviceProcAddrreaches it.
- The loader — on Windows
-
Real OS threads behind
Job.*andSync.*—Job.parallel_forruns work across every core.fn name— an expression naming a top-level function as a value, for a worker's entry point.
No closures: the data a worker needs is passed in.Job.parallel_for(count, fn work, ctx)—work(i, ctx)for everyiin[0, count), split
into chunks across a worker pool (one thread per core but one, pthreads or Win32) and the calling
thread; returns when all are done.Job.is_worker()says whether code is on a pool thread.Sync.*is thread-safe — mutexes are real mutexes, atomics are compare-and-swap, channels are
guarded, andSync.cpu_count()reports the machine's cores.- A worker must not change the world —
spawnanddespawnon a pool thread stop the program with
a located message.
-
Texture filtering and shadow resolution as settings a game can change while it runs.
r3d_set_anisotropy(level)sets the anisotropic filtering of every mipmapped texture already
loaded, not only of later uploads, on OpenGL and Vulkan.shadow_set_res(size)remakes the cascades' depth layers at 1024, 2048 or 4096 (shadow_res
replaces theSHADOW_RESconstant); the lighting reads the texel size from the map.
-
app native "<dir>"inpackage.ludic—ludic bundleon Windows copies that directory's files beside
the executable: native libraries loaded at run time (NVIDIA Streamline's DLLs, say) and their licence texts,
which cannot live in the pack.
Fixes
- The meadow's grass draws on the Vulkan renderer again — the chunked grass path uploaded its
vec4tile
table withu_fv, which on Vulkan copies one float per array element, so every tile had no blades.
u_f4vuploads arrays ofvec4.
Performance
- The sky's light is baked once at start, not twice. A game that turns the sky at boot sets
sky_start_yawbeforer3d_init, andsky_loadbakes the image-based light at that yaw; turning to
a yaw the light is already baked at (sky_set_yaw) no longer bakes it again. Without it, nothing
changes.
Downloads
-
Source code (ZIP)
0 downloads
-
Source code (TAR.GZ)
0 downloads
-
ludic-0.16.0-linux-x86_64.tar.gz
0 downloads · 1.7 MiB
-
ludic-0.16.0-linux-x86_64.tar.gz.sha256
0 downloads · 99 B
-
ludic-0.16.0-src.tar.gz
0 downloads · 3.5 MiB
-
ludic-0.16.0-src.tar.gz.sha256
0 downloads · 90 B
-