A cross-platform C++20 framework that abstracts native OS primitives behind a single, modern API. eacp lets you write desktop and mobile applications once and have them target the platform's first-class primitives directly. The heavy lifting stays with the OS: there is no bundled renderer, no bundled widget toolkit and no VM. That reaches the GPU too: a shader is a C++ struct, emitted as Metal, HLSL and GLSL from one source, and the pipeline around it is one API over all three backends.
eacp wraps the platform's native building blocks rather than reimplementing them, so apps inherit the look, feel, and performance of the host OS:
- Application lifecycle — a templated
Apps::run<T>()entry point that wires up the platform's main event loop. - Command-line tools —
Apps::runConsoleruns a tool's body inside the loop and turns an escaping exception into an exit code,Args::Parserbinds options straight to your variables and writes the help text, andConsole::print/printErrorwrite plain lines to stdout and stderr. - Event loops & threading —
EventLoop,Timer,DisplayLink, andcallAsyncon top of CFRunLoop / NSTimer / CADisplayLink, their Windows equivalents, and on Linux oneepolldescriptor a plugin host's own loop can pump (getEventLoopFd,pumpEventLoop). - Files —
FilePathcarries a path as UTF-8 text with/separators on every platform (nativeStringandfromNativeconvert to and from the platform's own form,\on Windows),Files::readFile/writeFile/writeFileAtomicallymove whole files,FileandMemoryMappedFileread big ones in pieces, andZipreads and writes archives, extracting one entry or all of them straight to disk a chunk at a time, with a progress callback that can cancel, so a multi-gigabyte archive unpacks in bounded memory. A directory is walked withFiles::forEachEntry(a visitor that can prune a subtree or stop early, with unreadable entries reported to a callback) or collected withlistDirectory/listFiles, recursively or not, hidden entries and symlinks by choice, filtered by a wildcard such as"*.wav; *.flac".copyFile,copyDirectory,moveFile,removeFile,appendFileandwriteFileAtchange files and report failure asfalse;FileWriterstreams into one,TemporaryFiledeletes its path when it goes out of scope, andlegalFileName/nonexistentSiblingname new ones.Files::openWithSystemhands a file to its default application or opens a folder in Finder, Explorer or the desktop's file manager, andrevealInFileManagershows an item there selected (Linux opens the folder holding it); both answerfalseon iOS and Android. - Graphics —
Window,View,Path,Font, and aContextdrawing abstraction backed by Core Graphics / CoreText on Apple platforms and the native Windows graphics stack.primaryDisplay()reports the screen's frame and work area in points, so an app can pick a first window size that fits;View::getWindow()lets a view reach the window it is in rather than be handed it.setMouseCursortakes one of the named shapes or aMouseCursormade from anImage, a hotspot and how many of its pixels make a point, so a 32-pixel brush can be an 8-point pointer that stays sharp on a Retina screen (macOS and Windows; the arrow elsewhere). Files dragged in from Finder, Explorer or a DAW's browser reach a view that callssetAcceptsFileDrops()asfileDragEnter/fileDragMove/fileDragExit/filesDroppedwith UTF-8 paths, and aUI::ComponentHosthands them to the deepestUI::FileDropTargetcomponent under the pointer that wants them (macOS and Windows). Going the other way,startFileDrag(paths, options)from a view's or a component's mouse handler drags files out to Finder, Explorer or a DAW, optionally letting the receiver move them and reportingCancelled,CopiedorMovedonce it ends (macOS and Windows). A trackpad pinch reaches the view under the pointer asmouseMagnify, each step amagnifyScaleto multiply the zoom by (1.25 is a quarter larger) with the gesture's Began / Changed / Ended inscrollPhase, and aUI::ComponentHosthands it to the component under the pointer and up the tree until aComponent::mouseMagnifyreturns true, as it does the wheel (macOS, and touch-screen zoom gestures on Windows). On a touch screen a view that callssetHandlesTouchEvents()gets every finger as its ownTouchEvent(touchBegan/touchMoved/touchEnded, one id per finger, held by the view it came down on, with its contactradiusin points), and any other view gets the first finger as the mouse, so widgets written for a mouse work unchanged.View::getSafeAreaInsets()is what the status bar, a notch or the home indicator covers, withsafeAreaInsetsChanged()when it moves; both stay zero and silent on desktop windows.Apps/UI/TouchDemodraws both. AUI::ComponentHostlays its root inside the safe area by default (setRespectsSafeArea(false)gives it the whole view), andScrollPanelandListBoxscroll by finger, coasting on when flung, while a mouse drag over them means what it always did. - Game input —
GameInputis keyboard and mouse state for a game loop, polled once a frame beside theViewcallbacks that UI and text entry keep using:snapshot()returns a frame that answersisDown,wasPressed,wasReleasedandmouseDelta, with every event timestamped on the clockFrameTimeuses. On Apple platforms the events come from the GameController framework off the main thread, so they are captured while the app is still drawing; elsewhere they come from the window's own events. Input counts only while the window has key focus, and losing it releases every key.frame.gamepads()lists the connected controllers — buttons named by position, sticks and triggers as raw axes — from GameController on Apple and XInput on Windows, so an Xbox controller on USB or Bluetooth works on both and a Switch Pro Controller on macOS.Apps/GPU/Mazeis a first-person maze driven by it, andApps/UI/GamepadDemois every button and axis of a controller doing something on screen and announcing itself. - GPU —
GPUView, frames, passes, buffers, textures and pipelines over Metal and D3D12, plus compute — and a shader EDSL that makes a shader a C++ struct rather than a string literal per backend. The same compute kernel also runs on the CPU, on the calling thread and without allocating, where no device came up or an audio callback needs it now. SeeLib/eacp/GPU/README.md. - ML — tensor-level compute as a
Graphof whole-tensor ops (linear,matmul,softmax,layerNorm,conv,gather, attention with a causal or run-time mask,argmax, andapplyfor anything elementwise, written over the shader EDSL's ownFloat), written out as an.mlpackagethat Core ML compiles and runs on the CPU, the GPU or the Apple Neural Engine.Modelcaches the compile, predicts blocking or as aThreads::Async, reports where each op was placed, and itsMultiArraycopies to and from aGPU::Buffer. The graph builder and writers build everywhere; only the runner is Apple. SeeLib/eacp/ML/README.md. - Widgets & menus — native text inputs, menus, and embedded views.
- WebView — embed a system web view (WKWebView on Apple, WebView2 on Windows) with support for popups and new-window requests.
- Networking — an
HTTP::Request/HTTP::ResponseAPI plus anHTTPServer, aWebSocket::Connectionclient andWebSocket::Server, TCP sockets, IPC channels and an RPC layer over both —Apps/Network/WebSocketDemoruns both WebSocket ends in one process. Backed by NSURLSession and Network.framework on Apple platforms, WinHTTP on Windows, libcurl on Linux and Java's ownHttpURLConnectionand sockets on Android.OnlineResourcefetches a file an app needs into its own Application Support folder once, revalidates it against the server's ETag on later runs, and unpacks a zip;UI::OnlineResourceMonitorshows every such fetch as a list with progress bars.Apps/Console/OnlineResourceandApps/UI/ResourceMonitorare the two examples. - SVG — SVG documents parsed (
SVG::parseXML) and drawn bySVG::SVGComponent, aUIcomponent, or rendered off-screen to an image (SVG::renderToImage), the same on every platform: linear and radial gradients, clip paths, element and group opacity,<use>/<symbol>, nested<svg>,preserveAspectRatio, dashes, thestyleattribute, text, and<image>from a base64 PNG or JPEG data URI, drawn directly or as a<pattern>fill. Not<mask>,<style>selectors or filters. - Processes & plugins — launch a child process with args, env and working
directory, feed its stdin and capture its output, separately or with stderr
merged into stdout, and optionally watch it arrive through a callback
(
eacp::Processes). A whole command line can be given instead of executable and arguments: Windows hands it toCreateProcessWverbatim (socmd /c "a.bat && b.exe"works), POSIX splits it into argv the way JUCE'sStringArray::fromTokensdoes and runs no shell. Load and unload shared libraries at runtime (DynamicLibrary). Both directions of plugin UI are covered:EmbeddedViewputs a view tree of ours inside a window a host owns (anNSView, aUIViewsuch as an AUv3's view controller hands over, anHWNDor an X11 window), andNativeChildSurfaceis the inverse, aViewin one of our layouts that hands a foreign toolkit anNSView*or a childHWNDto parent its own editor into — what a plugin host passes toIPlugView::attached().EmbedderKeyForwarderandKeyGrabcarry the keys a hosted editor does not want back to the host's main window, so a DAW's shortcuts still work. - Text & sprites — font metrics, glyph rasterization and a GPU glyph atlas, alongside a batched textured-quad renderer for everything that draws in bulk.
- UI — a lightweight component tier: a whole widget tree in one
GPUView, drawn through the sprite and glyph batchers.UI::GPUComponentputs custom GPU rendering inside that tree: aGPUView-stylerender(Frame&)andupdate, drawn in the window's own frame and then clipped, scrolled, overlapped, faded and transformed like any other component, so a 3D viewport or a shader preview sits among the widgets rather than on top of them.Apps/UI/GPUComponentsputs up to 64 of them in one window. SeeLib/eacp/UI/README.md. - SIMD — the image hot loops run through ESIMD, a portable SIMD library with runtime backend dispatch that is also usable on its own.
- Maths —
Vec2/Vec3/Vec4and a column-majorMat4with the transform and projection builders, packed exactly as the shader types they register as, so the same value does the CPU-side geometry and crosses to the GPU as a vertex field or uniform without repacking. - Camera & video — capture devices and frames with a
CameraViewto show them, screen capture, video encoding, and decoded playback through the GPU display stack. - Interop helpers — RAII wrappers (
Ptr<T>,CFRef<T>,AutoReleasePool) for safe Objective-C / Core Foundation interop, plus generic utilities (Pimpl,Singleton, vector helpers).
The dividing line is drawing. Everything that never touches a screen — the app and threading core, processes, plugins, files, the HTTP client and server, IPC and RPC, the CPU compute interpreter, the ML graph builder — builds on Linux too, which is what makes eacp usable for a headless service as well as for a GUI. The graphics stack builds on all four platforms, because it wraps each one's own compositor instead of shipping one: Cocoa and Metal, Win32 and D3D12, UIKit, and Wayland or X11 with Vulkan.
| Module | macOS | Windows | iOS | Linux |
|---|---|---|---|---|
Core — lifecycle, event loops, timers, processes, plugins, files |
✅ | ✅ | ✅ | ✅ |
Network — HTTP client and server, WebSocket client, TCP, IPC, RPC |
✅ | ✅ | ✅ | ✅ |
Graphics — windows, views, widgets, menus, drawing |
✅ | ✅ | ✅ | ✅ † |
GameInput — polled keyboard, mouse and gamepads for a game loop |
✅ ‡ | ✅ ‡ | ✅ ‡ | ✅ |
GPU / GPUWidgets — Metal, D3D12, Vulkan and the shader EDSL |
✅ | ✅ | ✅ | ✅ |
CpuCompute — the same compute kernels run on the CPU, no device needed |
✅ | ✅ | ✅ | ✅ |
Text / Sprites — glyph rasterization, atlas, batched quads |
✅ | ✅ | ✅ | ✅ |
UI / SVG — component tier and SVG rendering |
✅ | ✅ | ✅ | ✅ † |
WebView — WKWebView and WebView2 |
✅ | ✅ | ✅ | — |
Camera / CameraView — capture devices and frames |
✅ | ✅ | ✅ | — |
Video / VideoView — screen capture, encode, playback |
✅ | ✅ | — | — |
ML — tensor graphs compiled and run through Core ML |
✅ | — | ✅ | — |
† Linux has no platform 2D tier: no Graphics::Context, Font, TextInput,
image codecs, menus or tray. Everything drawn through a GPUView — the UI
component tier, its text, its images and SVGComponent — works there in
full, and a Window is a real Wayland or X11 window with input, clipboard,
fractional scaling and mouse lock. Audio-plugin hosting has what it needs
(EmbeddedView into a host's X11 window, and an event loop a host's own loop
can pump), and EACP_HEADLESS=1 runs every window and every GPU test with no
display server at all. Docs/Linux.md is the full account.
‡ Keys and mouse are fed by the GameController framework on Apple platforms, which times events off the main thread, and by the window's own events elsewhere; gamepads come from GameController on Apple and XInput on Windows.
CI builds and tests macOS (universal), Windows x64 and ARM64 (MSVC and
clang-cl) and Linux (GCC, Clang, and a lane that runs the graphics stack on
Mesa's software Vulkan under a headless Weston and then an Xvfb), and builds
iOS for the simulator and Android (Vulkan 1.1; the HelloGPU and
HelloNetwork examples for arm64-v8a with a pinned NDK; the second checks the
HTTP client and server, downloads, OnlineResource and both WebSocket ends
when run on a device). macOS is the most exercised of them.
On Android, Camera and CameraView work as well, with the camera permission
asked for at run time (eacp::Android::requestPermission); Video and
VideoView are not available there yet.
Android builds through an Android Studio project the configure writes;
Apps/Android/README.md is the guide.
A minimal console app with a recurring timer:
#include <eacp/Core/Core.h>
using namespace eacp;
struct App
{
void update()
{
LOG(numTimes);
numTimes++;
if (numTimes == 4)
Apps::quit();
}
int numTimes = 0;
Threads::Timer timer {[&] { update(); }, 1};
};
int main()
{
return Apps::run<App>();
}A GUI app embedding a web view:
#include <eacp/WebView/WebView.h>
using namespace eacp;
using namespace Graphics;
WindowOptions windowOptions()
{
auto options = WindowOptions {};
options.title = "Browser";
options.width = 1100;
options.height = 760;
return options;
}
// A Window built with its view adopts it as its content, so the pair is two
// members and the constructor body is left for what the app actually does.
struct MyApp
{
MyApp() { webView.loadURL("https://example.com"); }
WebView webView;
Window window {webView, windowOptions()};
};
int main()
{
return eacp::Apps::run<MyApp>();
}An app that is one view in one window and nothing else needs no struct at all:
Graphics::runWindowedApp<MyView>(options, viewArgs...) runs a
ViewWindow<MyView> — the view built from viewArgs, then the window showing
it — as the app.
#include <eacp/Graphics/Graphics.h>
using namespace eacp;
struct HelloView final : Graphics::View
{
void paint(Graphics::Context& g) override
{
g.setColor(Graphics::Color::white());
g.fillRect(getLocalBounds());
}
};
int main()
{
auto options = Graphics::WindowOptions {};
options.title = "Hello";
return Graphics::runWindowedApp<HelloView>(options);
}An HTTP request:
#include <eacp/Network/HTTP/Http.h>
auto req = eacp::HTTP::Request::post("https://api.example.com/posts", body);
req.headers["Content-Type"] = "application/json";
auto res = req.perform();Redirects are followed unless req.followRedirects = false, which hands back
the 3xx itself and sends nothing to the new address; req.maxRedirects caps
how many are followed before the request fails. req.maxResponseSize
caps the body: a response that grows past it is cut off as it arrives and
comes back as an error.
req.timeout bounds the whole request. req.connectTimeout bounds getting
connected (on Apple platforms, which report no connect event, it runs until
the response headers arrive), and req.idleTimeout fails a transfer that goes
quiet for that long without capping how long a steady one may run - the way to
catch a stalled download of any size. No backend answers from a local cache
unless req.cachePolicy = HTTP::CachePolicy::UseLocalCache.
A request sends the platform's own User-Agent - CFNetwork's on Apple platforms,
HttpURLConnection's on Android - and eacp/<version> (HTTP::eacpUserAgent())
on Windows and Linux, which have none. HTTP::setDefaultUserAgent("MyApp/1.2")
replaces that for every request on every platform, and an empty string puts it
back; a User-Agent header on the request beats both:
eacp::HTTP::setDefaultUserAgent("MyApp/1.2");
auto req = eacp::HTTP::Request("https://api.example.com/");
req.headers["User-Agent"] = "MyApp/1.2 (sync)"; // this request onlyreq.resumeDownload makes downloadTo continue a partial file: it asks for
the bytes it lacks with a Range header, appends a 206 and counts the bytes
already there in its progress, and starts over when the server sends the whole
file instead.
HTTP::URL builds an address from a base and an ordered list of query
parameters, encoding each once:
auto url = eacp::HTTP::URL("https://api.example.com/search");
url.addParameter("q", "rock & roll").addParameter("page", "2");
auto res = eacp::HTTP::Request(url.toString()).perform();Saving a file under a name the user typed, without overwriting anything:
using namespace eacp;
auto folder = FilePath::documentsDirectory() / "Presets";
auto name = Files::legalFileName("Kick: 808 / Hard"); // "Kick 808 Hard"
auto path = Files::nonexistentSibling((folder / name).withExtension("preset"));
Files::writeFileAtomically(path, bytes);
auto options = Files::DirectoryOptions {};
options.pattern = "*.wav; *.flac";
auto samples = Files::listFiles(FilePath::musicDirectory(), options);More examples live under Apps/, grouped by the module they exercise:
Console, Network, Graphics, GPU, UI, SVG, WebView, Camera,
Video, Plugins and Mixed.
There is no shader string anywhere in an eacp app. A shader is a struct that
derives from ShaderProgram; define() records a graph of typed value handles,
and the emitters turn that one graph into Metal Shading Language for macOS and
iOS, into HLSL for Direct3D 12 on Windows and into GLSL for Vulkan on Linux,
where glslang compiles it to SPIR-V inside the binary. Vertex inputs are
pulled straight out of the CPU vertex struct, so that struct is the vertex
layout; uniforms and textures are typed members assigned by name, and
Maths::Vec2 crosses to the GPU packed exactly as the float2 it registers
as.
#include <eacp/GPU/GPU.h>
using namespace eacp;
using namespace GPU;
struct Vertex
{
Maths::Vec2 position;
Maths::Vec2 uv;
};
struct Waves final : ShaderProgram
{
Waves() { compile(); }
void define() override
{
auto position = vertexInput(&Vertex::position);
auto uv = varying(vertexInput(&Vertex::uv));
auto ripple = 0.5f + 0.5f * sin(uv.x() * 8.f + time);
setPosition(float4(position, 0.f, 1.f));
auto texel = sample(image, uv);
setFragment(texel * float4(ripple, uv.y(), 1.f, 1.f));
}
Uniform<Float> time;
Uniform<Texture2D> image;
EACP_SHADER(time, image)
};The view that draws it is as portable as the shader. setVertices uploads the
typed vertex array, prepare takes a RenderPipelineDescriptor — sample
count, depth test and write, blend equation, cull mode, winding — and builds
the pipeline state from it, and pass.draw(shader) binds the pipeline, the
vertices, the uniform block and every assigned texture:
struct WavesView final : GPUView
{
WavesView()
: image(loadTexture())
{
shader.setVertices(quad);
shader.prepare({.sampleCount = sampleCount(),
.blendMode = BlendMode::AlphaBlend});
shader.image = image;
setContinuous(true);
}
void update(Threads::FrameTime time) override
{
elapsed += static_cast<float>(time.delta);
}
void render(Frame& frame) override
{
shader.time = elapsed;
auto pass = frame.beginPass({});
pass.draw(shader);
}
Texture image;
Waves shader;
float elapsed = 0.f;
};Render targets, depth and multisampling, and compute passes all sit on the same
Frame, ordered for you, with no fences to write.
The EDSL covers the Float, Int, UInt and Bool families and the
matrices, every swizzle, the intrinsic set spelled the way MSL and HLSL spell
it, var / select / ifThen / loop, 2D, cube and depth textures, storage
buffers readable from either stage, instancing, and compute — ComputeProgram
is the same struct shape, with atomics, threadgroup memory, barriers, SIMD-group
matrix fragments, packed fp16, bf16, int8 and int4 weight reads and a dispatch
the GPU sized. What the backends cannot pack the same way — a Bool or a
Float3x3 uniform — is a static_assert rather than a footnote.
Lib/eacp/GPU/README.md is the full account, and
Apps/GPU has a worked example of every piece.
The graph a kernel records is also what runs it where there is no device.
The CPU compute interpreter runs a ComputeProgram on the calling thread over
plain arrays, allocation-free and realtime-safe, so an audio callback or a
driverless Linux box runs the very same kernel the GPU does, and every emitter
test has a numeric half that needs no driver — Apps/GPU/CpuCompute times the
two against each other. One tier up, Lib/eacp/ML takes tensor-level compute
written against the same buffers and compiles it to a Core ML program, so a
net that runs through the compute kernels on every platform also runs on the
Apple Neural Engine where there is one (see
Lib/eacp/ML/README.md).
eacp uses CMake (3.31+) and a C++20 toolchain. Dependencies are fetched via
CPM automatically at configure time
— among them ESIMD, the SIMD kernels
the image operations run through — except
miniz, which is carried in
ThirdParty/ and wrapped by eacp::Zip, and
fmt, carried beside it and used internally.
cmake -G Ninja -B build -DCMAKE_BUILD_TYPE=Debug
cmake --build buildBuild a specific example:
cmake --build build --target GUI # build/Apps/GUI/GUI.app
cmake --build build --target Console # build/Apps/Console/ConsoleOn Linux the window systems, the text stack and the HTTP client are found by pkg-config against the machine's own libraries, and the Vulkan loader is opened at runtime, so a driver is all the GPU half needs:
sudo apt install pkg-config libcurl4-openssl-dev \
libwayland-dev wayland-protocols libwayland-bin libxkbcommon-dev libdecor-0-dev \
libxcb1-dev libxcb-xkb-dev libxkbcommon-x11-dev libxcb-randr0-dev \
libxcb-xfixes0-dev libxcb-cursor-dev libxcb-icccm4-dev libxcb-xinput-dev \
libfreetype-dev libharfbuzz-dev libfontconfig-dev mesa-vulkan-driversThe options most builds reach for:
EACP_UNITY_BUILD(defaultOFF) — unity builds, markedly faster for a cold full build, off by default because they collapse the per-file entries incompile_commands.jsonthat language servers read.EACP_BUILD_GRAPHICS(defaultON) — turn it off to build only the portable modules, on any platform.EACP_BUILD_SPIRV(defaultONon Linux,OFFelsewhere) — the GLSL compiler the Vulkan backend ships. PassingONon a Mac or a Windows machine turns the GLSL compile checks in the GPU test suites on.
Every option, the CPM and pkg-config dependencies, the capability variables
that decide which modules a platform gets, and how CI runs are in
Docs/Build.md.
Each subdirectory of Lib/eacp is a self-contained area you can include on its
own — take Network without pulling in GPU.
Lib/eacp/
Core/ App lifecycle, threading, processes, plugins, files, vector maths,
ObjC/CF interop
Network/ HTTP client and server, WebSocket client, TCP, IPC, RPC
Graphics/ Windows, views, widgets, menus, drawing primitives
GPU/ Metal / D3D12 / Vulkan: device, buffers, textures, pipelines,
passes, the shader EDSL and its CPU interpreter — see GPU/README.md
GPUWidgets/ Views drawn on the GPU (gradients, paths)
ML/ Tensor graphs compiled and run through Core ML — see ML/README.md
Text/ Font metrics, glyph rasterization and a GPU glyph atlas
Sprites/ Batched textured-quad renderer
UI/ A whole widget tree in one GPUView, GPU components included —
see UI/README.md
SVG/ SVG parsing and rendering
WebView/ System web view embedding
Camera/ Capture devices and frames, plus CameraView/ to show them
Video/ Screen capture and encoding, plus VideoView/ for playback
Apps/ Example applications
Tests/ Unit tests
Docs/ In-depth documentation: the build system and the Linux backend
ThirdParty/ Vendored libraries (miniz, behind eacp::Zip; fmt)
CMake/ Build helpers (TargetSetup, CPM)
Docs/Build.md— every build option, the dependencies, the capability variables and the CI matrix.Docs/Linux.md— the Linux backend: Wayland, X11, Vulkan and FreeType, what is and is not there, and the environment variables that drive it.Lib/eacp/GPU/README.md— the GPU module and the shader EDSL end to end: pipelines, render targets, compute, packed weights, the SIMD-group matrix, running a kernel on the CPU, and the Vulkan backend.SAMPLERS.mdbeside it is the sampler model.Lib/eacp/UI/README.md— the component tier: how a frame is built, andGPUComponent, custom GPU rendering inside a component tree, with what it costs on and off screen.Lib/eacp/ML/README.md— the Core ML tier: building a tensor graph, running a model, the compile cache and what was measured.Apps/GPU/VariableFont/README.md— the variable-font example.
Four projects lean on different parts of the framework, and between them are what keeps it honest — each one is a demand the API has to meet, and the gaps they surface are what gets fixed next.
-
PureDOOM — DOOM on eacp's application, GPU and input stack. The level is drawn as real hardware 3D at the window's resolution rather than at 320×200, but the shading is DOOM's own: the texture yields a palette index, the
COLORMAProw picked by sector light and distance remaps it, and the palette resolves the colour. Three attract-mode demos, 11,410 tics, replay as a test that hashes the world after every tic. -
ShaderToyEACP — Shadertoy's GLSL turned into eacp GPU programs, shaders authored as C++ structs and compiled to Metal and HLSL from one source. A corpus of two hundred real fragment shaders turns "what is the shader EDSL missing?" into a measurement: every shader that fails to convert names a gap, and the number blocked on each gap decides which one to close next.
-
imgui-eacp — Dear ImGui as an ordinary eacp
View, drawn by the GPU module, with ImGui's shader written once in the EDSL and emitted as both MSL and HLSL. Because it is a real view it composes:Apps/MixedViewsputs it beside aWebViewin one window, wired in both directions. -
WhisperEACP — Whisper on eacp's compute stack: the encoder and decoder as EDSL compute kernels over packed fp16 weights, running on Metal, D3D12 and Vulkan from one source, and the encoder again through
Lib/eacp/MLon Core ML. It is what drove the SIMD-group matrix, the packed weight reads and the Core ML backend, and its tests are what say the two ways of running the same net agree. -
CowTerm — a GPU-accelerated terminal emulator and session manager by Jamie Pond. Every visible pixel is composited on the GPU from a CoreText glyph atlas; sessions, a fuzzy palette and persistent pane layouts replace the tmux-sessionizer workflow.
eacp is developed in the open. Issues, patches and experiments are all welcome;
the examples under Apps/ are the fastest way in.
MIT — see LICENSE.