Reactivity
Every IC component has a MATLAB side and a frontend side. When you set a property in MATLAB, the UI updates. When the user interacts with the UI, MATLAB gets notified. This bidirectional synchronization is what we call reactivity, and it applies to three things: properties, methods, and events.
Properties
A reactive property stays in sync between MATLAB and the frontend automatically. Reactive properties are tagged with Description = "Reactive" and SetObservable in the component class, and the framework takes care of the rest.
The demo below shows a live ic.Slider. You can interact with it in two ways: drag the slider directly, or type commands in the MATLAB console below it. Both directions update the same state, just like in a real IC application.
Try typing slider.Value = 75 in the console to move the slider from MATLAB. Then drag it back and type slider.Value to read the updated position. You also have full control over the slider’s Min, Max, Step, and Disabled properties. Change them in the console and see how the UI reacts immediately.
This is essentially what the MATLAB code below does, just without the interactive console:
slider = ic.Slider("Value", 40, "Min", 0, "Max", 100);
frame.addChild(slider);
slider.Value = 75;
slider.Max = 200; Properties are not immediately synched in any direction.
- MATLAB properties are sent to the frontend on the next
drawnowevent. This happens either when MATLAB is not busy, or when specific functions that flush the graphics queue are called (drawnow,pause,wait, etc.). That means, for example, that if you set the following in MATLAB, only the final value (10) will have an effect on the frontend.
slider.Value = 75;
slider.Value = 10; - Frontend properties are debounced and sent to MATLAB after a 50ms pause in user interaction (see below).
Debouncing
There is a subtlety to the frontend-to-MATLAB direction that is worth understanding early. When the user drags a slider, the frontend fires value changes at a very high rate. Sending every single one to MATLAB would flood the communication channel. To avoid this, the framework debounces all property changes from the fronted at 50ms. Debouncing means waiting until the value stops changing for a given amount of time, and then sending only the most recent value.
The chart below illustrates this. The red dots are the individual change events from the browser (dozens per second during a drag). The blue dots are what MATLAB actually receives: one update after each 50ms pause.
In practice, this is invisible for most use cases. But it has one important consequence that is discussed in the section for how to add event listeners.
Methods
Reactive methods let you call a function that runs on the frontend from MATLAB. Focusing an input, scrolling to a position, selecting text: these are things that only the browser can do, and methods are how you trigger them.
Every reactive method returns an ic.async.Promise. The promise resolves when the frontend finishes executing and sends back an ic.async.Resolution with two fields: Success (logical) and Data (the return value, or an error message if something went wrong).
Think of this as an asynchronous function call: you ask the frontend to do something, but the communication is not instant, since MATLAB waits for a drawnow event to send the request. When the frontend receives the command, it executes it and sends back the result, which will take additional time. And only after MATLAB receives the response and processes it, the promise resolves and your code can react to it.
What it means is that you cannot get the result of a method call immediately. You have to wait for the promise to resolve first. This is a fundamental aspect of how the IC framework works, and it is important to design your code with this in mind from the start.
There are 2 main ways to work with promises (but you should read the ic.async.Promise documentation for more details and advanced patterns):
- You can block MATLAB until the result arrives, using the
waitmethod. - Or you can write non-blocking code that reacts to the result whenever it comes, using the
thenmethod.
Waiting
The wait call blocks MATLAB until the promise resolves or a timeout elapses (2 seconds in this example).
p = editor.focus();
p.wait(2);
if p.isResolved()
res = p.get();
disp(res.Success);
disp(res.Data);
end Writing non-blocking callbacks
The then method takes a callback that runs when the promise resolves. This allows you to write non-blocking code that reacts to the result whenever it comes, without freezing the MATLAB session.
editor.focus().then(@(res) ...
disp("success: " + string(res.Success))); Events
Events are one-directional notifications from the frontend to MATLAB. A button fires Clicked when pressed, a slider fires ValueChanging while being dragged, an input fires ValueChanged when the user commits a change. You receive them through MATLAB’s standard addlistener.
Drag the slider or click the button and watch the event log:
addlistener(btn, "Clicked", @(~, evt) ...
disp(evt.Data.value.timestamp));
addlistener(slider, "ValueChanging", @(~, evt) ...
disp(evt.Data.value)); Events have an important optimization called lazy activation: the frontend only starts publishing a given event after at least one MATLAB listener is attached. When the last listener is removed or goes out of scope, publishing stops automatically. This avoids unnecessary traffic for events nobody cares about.
You can attach multiple listeners to the same event and remove them independently:
l1 = addlistener(slider, "ValueChanged", @(~, evt) ...
disp("new value: " + string(evt.Data.value)));
l2 = addlistener(slider, "ValueChanged", @(~, evt) ...
updatePlot(evt.Data.value));
delete(l1); Listeners
Event listeners
This is the single most common mistake when working with IC components, and it comes from the debouncing discussed earlier.
When a reactive event fires, the event data carries the value at the exact moment the event was triggered on the frontend. But because property updates are debounced, the MATLAB property might not have caught up yet. If your listener reads the property from the source object instead of the event data, you might get a stale value.
Drag the slider below. The left log reads src.Value (the property, which lags behind due to the debounce), while the right log reads evt.Data.value (the event payload, which is always current). Notice how the left side occasionally shows outdated numbers:
Wrong: reading the property from the source object.
addlistener(slider, "ValueChanging", @(src, ~) ...
disp("value: " + string(src.Value))); Correct: reading the value from the event data.
addlistener(slider, "ValueChanging", @(~, evt) ...
disp("value: " + string(evt.Data.value))); The event payload is always in sync with what the user did. The property will catch up eventually, but the event data is the authoritative source at the time the callback runs. Build this habit early and you will avoid a class of subtle, hard-to-reproduce bugs.
Property listeners
Since all reactive properties are declared with SetObservable, you can also attach standard MATLAB property listeners to them using the PostSet event. This is useful when you want to react to property changes from either direction (MATLAB code or user interaction) in a single callback.
addlistener(slider, "Value", "PostSet", @(~, ~) ...
disp("Value is now: " + string(slider.Value))); Keep in mind that these PostSet callbacks fire with the debounced value when the change comes from the frontend. If you need the immediate value during a continuous interaction (like a drag), use the component’s events instead.