This guide walks you through the process of creating a new Image Operation (IOP) module in darktable.
Create a new C file in src/iop/. Use the template useless.c as a reference.
Example: src/iop/mymodule.c
For a full API reference, see IOP_Module_API.md.
Add your module file to the list:
add_iop(mymodule "mymodule.c")Add your module to all relevant pipeline order lists (e.g., legacy_order, v30_order, v50_order, v30_jpg_order, v50_jpg_order).
You must decide where your processing should happen (before or after which other operations).
// Example: placing it after exposure
{ { 12.0f }, "exposure", 0 },
{ { 13.0f }, "mymodule", 0 },You must also inject your module into dt_ioppr_get_iop_order_list(), which runs dynamically when upgrading or falling back. Find the _insert_before section and add your module:
_insert_before(iop_order_list, "module_it_should_run_before", "mymodule");Troubleshooting "missing iop_order for module" Fatal Error:
If darktable crashes on startup with [dt_init] ERROR: iop order looks bad, aborting. and logs missing iop_order for module mymodule, it means your module was registered in CMakeLists.txt but the iop string name does not perfectly match its placement in iop_order.c. Make sure:
- You have correctly defined the string identifier (e.g.,
"mymodule") in everyiop_ordertable array. - You added an
_insert_before()rule indt_ioppr_get_iop_order_list().
- Parameters:
dt_iop_mymodule_params_t- Use
DT_MODULE_INTROSPECTIONmacro. See introspection.md for metadata tags and versioning. - Define fields with default values and ranges in comments.
- Use
- GUI Data:
dt_iop_mymodule_gui_data_t(if needing GUI).
name(): unique internal name.default_colorspace(): usuallyIOP_CS_RGBorIOP_CS_LAB.process(): The core logic.
See IOP_Module_API.md — Required Functions for details.
description(): Tooltip description.flags():IOP_FLAGS_SUPPORTS_BLENDING, etc.default_group(): Which tab it belongs to (Technical, Grading, etc.). See Module_Groups.md.gui_init(): Create widgets.gui_update(): Sync widgets from params.commit_params(): If you need to precalculate data for processing.
See IOP_Module_API.md — Optional Functions for the full list.
In gui_init():
- Use
dt_bauhaus_slider_from_params(self, "param_name")to create sliders linked to your params. - These will automatically handle history updates, undo/redo, and shortcuts.
For the full GUI architecture (layout API, event flow, thread safety, widget reparenting), see GUI.md.
Ensure your module is in the correct place in iop_order.c.
- Scene-referred modules should generally be earlier.
- Display-referred modules should be later.
Note: Pipeline ordering affects commit_params() execution order. Modules that share state (e.g., via dev->chroma) depend on earlier modules having committed first. See pixelpipe_architecture.md for details.
By using dt_bauhaus widgets, shortcuts are automatically supported.
- Verify in Preferences > Shortcuts.
- See Shortcuts.md for manual registration and the action system.
Implement default_group() to place your module in the right tab.
IOP_GROUP_TECHNICAL: Technical corrections.IOP_GROUP_GRADING: Color/Tone grading.IOP_GROUP_EFFECTS: Aesthetic effects.
To allow your widgets to be used in the QAP:
- Use standard
dt_bauhauswidgets. - No extra code needed; users can add your widgets to their QAP config.
- Ensure your widgets make sense in isolation (have clear labels/tooltips).
- See Quick_Access_Panel.md for developer considerations and restrictions.
- Compile:
./build.sh - Run:
./build/bin/darktable -d pipe - Check terminal for any introspection errors (e.g. invalid default values).
- Open an image and verify your module appears and processes correctly.