Micro Interactive C Framework (MIC)

MIC is a tiny (<300 cloc) framework to boostrap interactive C projects. Application code can be dynamically reloaded upon compilation, allowing C to be used as a pseudo-scripting language. This property can be very useful when developing, but might not be always desirable for a final release, for this reason, static compilation can be selected as a make target when needed. The main limitation of hot code reload is that global state can't be stored in the main app code, but the framework is designed so the state is passed around when needed and is always accessible for the relevant functions.

This framework can be easily extended to work with multiple platforms. However, hot code reload will only be available for platforms that support dynamic linking. For the moment, only POSIX compliant platforms are supported but Windows is a target for the near future.

Remember that MIC is only a base, you can (and should) modify anything in here to adapt it to your particular project.

Example usage

Compile and execute the program:

make && ./build/app

While the application is running, make changes to the src/app.c. For example, change the app_step function from:

static inline bool
app_step(AppState *state, PlatformAPI platform) {
    (void)state; // Unused parameter.
    return true;


static inline bool
app_step(AppState *state, PlatformAPI platform) {
    (void)state; // Unused parameter.
    platform.log("Hello world!");
    return true;

In another terminal (or using your editor of choice), recompile the program:


The changes should take effect in the running application.

Implementation details

This framework offers two APIs for managing system resources and executing application logic. The PlatformAPI is described in src/platform.h and it's tasked with manage the resources of the platform and/or operating system. For example, opening/reading files, allocating memory, logging, etc. To support a new platform it is sufficient to implement the functions described on the API and make sure to select such platform at compile time. Additionally, the following internal functions must be implemented for static and dynamic linking:

// Load the dynamic library and initialize application. Returns the success or
// failure of this operation.
static bool _app_init(AppAPI *api, AppState *state, PlatformAPI platform);

// This function reloads the application code from the dynamic library,
// returning `true` if the AppAPI is ready to be used.
static bool _app_reload();

// Cleanup resources before exit.
static void _app_destroy(AppAPI *api, AppState *state, PlatformAPI platform);

The AppAPI, described in src/app.h and handles app initialization and destruction, it contains the behaviour in the event of hot code reloading as well as the main step function. The init function typically will be tasked with initial allocation of resources. The duty of reload and unload is application dependent, for example, we might wish to re-compile the shader programs or reload geometry/sprites when we make modifications to the main application code. The step function is called in every iteration of the loop, and is where the bulk of the logic will likely be.

The application state is stored in a unique AppState structure. MIC has been architected so that the state and platform functions are available in the previously mentioned AppAPI functions. AppState can contain whatever state is needed and will be persistent when re-compiling. The only limitation is that if the AppState structure changes, the application must be restarted, as the stored state will become invalid. This can be circumvented by pre-allocating some memory that can then be partitioned with a pool allocator to suit the application needs. This design is also conducive for saving state data by simply serializing the AppState structure.


I initially discovered the hot code reloading trick when watching Casey Muratori's Handmade Hero series. It blew my mind! However, the code he presented was focused solely on Windows and I never end up trying it myself. Some years later I tried to find a way of making this work in Mac/Linux when I came across Chris Wellons' take on this problem with a clever function pointer API in his interactive c demo page. This framework uses a lot of the ideas he presents there, as you can clearly see if you look at his code.