Build interactive web applications using HTML/JavaScript interfaces with MATLAB computational backends via the uihtml component. Use when creating HTML-based MATLAB apps, JavaScript MATLAB interfaces, web UIs with MATLAB, interactive MATLAB GUIs, or when user mentions uihtml, HTML, JavaScript, web apps, or web interfaces.
Installation
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Build interactive web applications using HTML/JavaScript interfaces with MATLAB computational backends via the uihtml component. Use when creating HTML-based MATLAB apps, JavaScript MATLAB interfaces, web UIs with MATLAB, interactive MATLAB GUIs, or when user mentions uihtml, HTML, JavaScript, web apps, or web interfaces.
license
MathWorks BSD-3-Clause (see LICENSE)
metadata
{"author":"MathWorks","version":"1.0"}
MATLAB uihtml App Builder
This skill covers how to build interactive web applications that combine HTML/JavaScript interfaces with MATLAB computational backends using the uihtml component. The HTML side handles the UI; MATLAB does the computation.
When to Use This Skill
Building interactive MATLAB apps with HTML/JavaScript interfaces
Creating web-based UIs for MATLAB applications
Building responsive MATLAB GUIs with HTML/CSS/JS
When user mentions: uihtml, HTML, JavaScript, web app, web interface, interactive GUI
Combining web UI design with MATLAB computational power
Creating calculator apps, data visualizers, or form-based MATLAB tools
Core Architecture
The Four Components
HTML Interface - User interface with buttons, forms, displays
JavaScript Logic - Event handling and UI interactions
MATLAB Backend - Computational engine and data processing
uihtml Component - Bridge between HTML and MATLAB
Communication Patterns
The uihtml component enables bidirectional communication between JavaScript and MATLAB through several mechanisms:
Pattern 1: MATLAB → JavaScript (Data Property)
Use Case: Sending data from MATLAB to update the HTML interface
% MATLAB side
h.Data = "Hello World!";
// JavaScript side
htmlComponent.addEventListener("DataChanged", function(event) {
document.getElementById("display").innerHTML = htmlComponent.Data;
});
Pattern 2: JavaScript → MATLAB (Events)
Use Case: Triggering MATLAB functions from user interactions
// JavaScript side - send event to MATLAB
htmlComponent.sendEventToMATLAB("Calculate", expression);
% MATLAB side - receive and handle event
h.HTMLEventReceivedFcn = @handleEvent;
function handleEvent(src, event)
eventName = event.HTMLEventName;
eventData = event.HTMLEventData;
% Process event...
end
Pattern 3: MATLAB → JavaScript (Custom Events)
Use Case: Sending computed results or status updates to JavaScript
% MATLAB side - send custom event to JavaScript
sendEventToHTMLSource(h, "ResultChanged", result);
// JavaScript side - listen for custom event
htmlComponent.addEventListener("ResultChanged", function(event) {
document.getElementById("display").textContent = event.Data;
});
Pattern 4: Complex Data Transfer
Use Case: Passing structured data between MATLAB and JavaScript
% MATLAB side - struct data gets JSON encoded automatically
itemData = struct("ItemName","Apple","Price",2,"Quantity",10);
h.Data = itemData;
// JavaScript side - access as object properties
htmlComponent.Data.ItemName// "Apple"
htmlComponent.Data.Price// 2
htmlComponent.Data.Quantity// 10
Important: decoding is automatic in both directions.
A JS object sent via sendEventToMATLAB arrives on event.HTMLEventData already converted to a MATLAB struct. Do not call jsondecode; it will fail on a struct.
A MATLAB struct sent via sendEventToHTMLSource arrives on event.Data already as a JavaScript object. Do not call JSON.parse; it will fail on an object.
Field names round-trip exactly: a JS {x0: 1} becomes a MATLAB struct('x0', 1), not struct('x_0', ...) or similar.
Numeric scalars arrive as MATLAB double. Wrap field reads with double(data.x0) if you want to be defensive about types.
Critical Rules
Security Requirements
ALWAYS set HTMLSource = 'trusted' when using local HTML files:
h.HTMLSource = fullfile(pwd, 'myapp.html');
% This is treated as trusted automatically for local files
MUST validate all input from JavaScript before processing in MATLAB
NEVER use eval() on user input without strict sanitization
ALWAYS restrict allowed characters in user input for expressions
Error Handling
ALWAYS wrap MATLAB event handlers in try-catch blocks:
function handleEvent(src, event)
eventName = event.HTMLEventName;
eventData = event.HTMLEventData;
try
% Process the event
result = processData(eventData);
% Send result back to JavaScript
sendEventToHTMLSource(src, 'ResultEvent', result);
catch ME
% Handle errors gracefully
fprintf('Error: %s\n', ME.message);
sendEventToHTMLSource(src, 'ErrorEvent', ME.message);
end
end
Data Validation
ALWAYS validate user input before processing:
function result = validateExpression(expression)
allowedChars = '0123456789+-*/.() ';
if ~all(ismember(expression, allowedChars))
error('Invalid characters in expression');
end
% Additional validation...
result = true;
end
File Organization
Follow this directory structure:
project/
├── app.m # Main MATLAB function
├── app.html # HTML interface
├── README.md # Usage instructions
└── examples/ # Additional examples (optional)
Complete Examples
Example 1: Simple Calculator App
MATLAB Side (calculator.m):
function calculator()
% Create main figure
fig = uifigure('Name', 'Calculator', 'Position', [100 100 400 500]);
% Create HTML component
h = uihtml(fig, 'Position', [25 25 350 450]);
h.HTMLSource = fullfile(pwd, 'calculator.html');
h.HTMLEventReceivedFcn = @(src, event) handleEvent(src, event);
end
function handleEvent(src, event)
eventName = event.HTMLEventName;
eventData = event.HTMLEventData;
try
switch eventName
case 'Calculate'
% Validate input
expression = char(eventData);
allowedChars = '0123456789+-*/.() ';
if ~all(ismember(expression, allowedChars))
error('Invalid characters in expression');
end
% Evaluate safely
result = eval(expression);
% Send result back
sendEventToHTMLSource(src, 'Result', num2str(result));
case 'Clear'
sendEventToHTMLSource(src, 'Result', '0');
end
catch ME
fprintf('Error: %s\n', ME.message);
sendEventToHTMLSource(src, 'Error', 'Invalid expression');
end
end
function formProcessor()
fig = uifigure('Name', 'Form Processor', 'Position', [100 100 600 400]);
h = uihtml(fig, 'Position', [25 25 550 350]);
h.HTMLSource = fullfile(pwd, 'form.html');
h.HTMLEventReceivedFcn = @(src, event) handleEvent(src, event);
end
function handleEvent(src, event)
eventName = event.HTMLEventName;
eventData = event.HTMLEventData;
try
switch eventName
case 'SubmitForm'
% Extract form data
name = eventData.name;
email = eventData.email;
age = eventData.age;
% Validate data
if isempty(name) || isempty(email)
error('Name and email are required');
end
if ~contains(email, '@')
error('Invalid email address');
end
if age < 0 || age > 120
error('Invalid age');
end
% Process data (example: save to file or database)
fprintf('Processing form:\n');
fprintf(' Name: %s\n', name);
fprintf(' Email: %s\n', email);
fprintf(' Age: %d\n', age);
% Send success message
result = struct('status', 'success', ...
'message', 'Form submitted');
sendEventToHTMLSource(src, 'FormResult', result);
case 'ClearForm'
sendEventToHTMLSource(src, 'FormCleared', '');
end
catch ME
fprintf('Error: %s\n', ME.message);
result = struct('status', 'error', 'message', ME.message);
sendEventToHTMLSource(src, 'FormResult', result);
end
end
Best Practices
UI Design Principles
The HTML in the examples below is deliberately minimal to keep the focus on the MATLAB↔JS wiring. The inline styling (Arial, #667eea → #764ba2 purple-blue gradients, generic cards) is not a design reference. Those are exactly the "AI slop" tells to avoid. For any panel a user will actually see, take the visual system from matlab-uihtml-design and follow its Design Guardrails.
Use CSS Grid or Flexbox for responsive layouts that adapt to different window sizes. Flexbox for 1D, Grid for 2D; don't default to Grid when flex-wrap is simpler
Implement hover effects for better user experience and visual feedback
Provide clear visual feedback for user actions (button clicks, form submission, errors), plus a visible :focus-visible state for keyboard users
Use semantic HTML elements (button, input, form) for better accessibility
Pick a color scheme deliberately (see matlab-uihtml-design for ready-made styles). Avoid the default reflexes: Inter/Roboto/Arial fonts, purple-to-blue gradients, side-stripe border-left accents, gradient text, and over-rounded (≥32px) cards
Verify contrast: body/label text ≥4.5:1, large text and UI boundaries ≥3:1. Give numeric readouts font-variant-numeric: tabular-nums so values don't jitter width
Respect reduced motion: wrap non-essential animation in @media (prefers-reduced-motion: reduce) with a crossfade/instant fallback
Performance Optimization
Minimize data transfer between HTML and MATLAB - send only necessary data
Use appropriate data types - numbers, strings, structs (converted to JSON)
Implement loading indicators for long MATLAB operations
Cache results when appropriate using persistent variables in MATLAB
Batch multiple updates instead of sending many small events
JavaScript builds expression strings from button clicks
Send expression to MATLAB via sendEventToMATLAB
MATLAB safely evaluates with input validation
Results sent back via sendEventToHTMLSource
Display results in real-time
Pattern 2: Data Visualization Pattern
JavaScript handles user interaction (sliders, dropdowns)
Send parameters to MATLAB for computation
MATLAB processes data and updates plots
Can use uiaxes for MATLAB plots or send data for JavaScript plotting
Support real-time updates and animations
Pattern 3: Form Processing Pattern
JavaScript collects form data into structured object
Send entire form data as single event
MATLAB validates each field
Process data (save, compute, export)
Send confirmation or error messages back
Update UI based on results
Pattern 4: Real-time Monitoring Pattern
MATLAB continuously generates data (simulation, sensor reading)
Send updates via sendEventToHTMLSource at intervals
JavaScript updates display in real-time
Implement start/stop/pause controls
Use efficient data formats (arrays, structs)
Long-Running Operations
When MATLAB drives an operation that takes more than a fraction of a second (an animation, an integration sweep, a streaming simulation), the patterns above need a few additions: a timer, resource cleanup, a way to cancel, and a way to tune the operation while it runs.
Timer-Driven Animation
Use a MATLAB timer to drive the operation. Store the handle on fig.UserData so other event handlers (Reset, Stop, figure-close) can find and stop it.
function startAnimation(fig, ax, data)
stopAnimTimer(fig); % kill any prior run
state.data = data;
state.idx = 1;
state.line = animatedline(ax, 'Color', [0.65 0.55 0.94], 'LineWidth', 1.4);
state.step = 5; % points per tick (mutable from outside; see below)
tmr = timer( ...
'ExecutionMode', 'fixedSpacing', ...
'Period', 0.03, ...
'BusyMode', 'drop', ... % skip ticks if callback overruns
'TimerFcn', @(s, ~) animStep(s, fig));
tmr.UserData = state;
fig.UserData.AnimTimer = tmr;
start(tmr);
end
function animStep(tmr, fig)
if ~isvalid(fig) || ~isvalid(tmr), return; end
s = tmr.UserData;
iEnd = min(numel(s.data), s.idx + s.step - 1);
addpoints(s.line, s.data(s.idx:iEnd, 1), s.data(s.idx:iEnd, 2));
s.idx = iEnd + 1;
tmr.UserData = s;
drawnow limitrate;
if s.idx > numel(s.data)
stop(tmr); delete(tmr);
fig.UserData.AnimTimer = [];
% Notify JS that the operation completed
sendEventToHTMLSource(fig.UserData.UIHtml, 'OperationDone', struct('n', iEnd));
end
end
Key choices:
BusyMode='drop': if a callback takes longer than Period, the next tick is skipped rather than queued. This prevents callback pile-ups on slow systems.
drawnow limitrate: caps rendering at ~20 fps while still processing UI events, so the user can still interact (scroll, drag sliders, click Stop).
Self-cleaning: the last tick stops and deletes the timer, then nulls out the stored handle.
Resource Cleanup on Figure Close
Any timer, listener, or background resource must be torn down when the figure closes. Otherwise it leaks across reruns and can fire callbacks against deleted graphics objects.
fig.UserData = struct('AnimTimer', [], 'UIHtml', h, 'Axes', ax);
fig.CloseRequestFcn = @(s, ~) closeFig(s);
function closeFig(fig)
stopAnimTimer(fig);
delete(fig);
end
function stopAnimTimer(fig)
if ~isvalid(fig) || ~isfield(fig.UserData, 'AnimTimer'), return; end
tmr = fig.UserData.AnimTimer;
if ~isempty(tmr) && isvalid(tmr)
try, stop(tmr); catch, end
delete(tmr);
end
fig.UserData.AnimTimer = [];
end
The same stopAnimTimer is reused by Reset and Stop handlers, so there's one cleanup path to maintain.
Cancellable Operations (Run ↔ Stop)
For operations the user might want to cancel, repurpose the primary action button via a JS state machine. Don't add a separate Stop button; the panel gets crowded, and a context-aware single button matches play/pause UX.
On the MATLAB side, StopOperation stops the timer, leaves any partial output in place (so the user keeps what was computed), and emits an event so JS can flip the button back to "Run":
case 'StopOperation'
stopAnimTimer(fig);
s = lastState(fig); % whatever partial result you tracked
sendEventToHTMLSource(src, 'OperationStopped', s);
Distinguish three completion events back to JS:
Event
When
OperationDone
Natural completion (full result)
OperationStopped
User clicked Stop (partial result)
OperationError
Caught exception (error message)
All three should call setRunning("idle") in JS so the button returns to "Run".
Live Control During an Ongoing Operation
When the user wants to tune a parameter while the operation runs (animation speed, simulation rate, plot range), the JS slider's input event needs to push updates to MATLAB without restarting the operation.
Two rules:
Throttle the JS-side send so dragging the slider doesn't flood MATLAB with events:
let sendTimer = null;
document.getElementById("speed").addEventListener("input", function() {
if (sendTimer) return;
sendTimer = setTimeout(function() {
const v = parseInt(document.getElementById("speed").value, 10);
window.htmlComponent.sendEventToMATLAB("SetSpeed", v);
sendTimer = null;
}, 80); // ~12 events/sec max
});
Patch the live timer's UserData in place. Don't stop and restart:
case 'SetSpeed'
tmr = fig.UserData.AnimTimer;
if ~isempty(tmr) && isvalid(tmr)
s = tmr.UserData;
s.step = max(1, min(50, round(double(data)))); % clamp + sanitize
tmr.UserData = s;
end
% no-op if no animation running; the slider still updates its label
MATLAB serializes all callbacks on the main thread, so the timer tick and the SetSpeed handler never execute concurrently. The UserData update is atomic from the tick's point of view.
Saving the Output (Export from a uiaxes)
A common requirement: a "Save Image" button in the HTML that exports the current plot. Use uiputfile + exportgraphics:
case 'ExportImage'
ax = fig.UserData.Axes;
if isempty(ax.Children)
sendEventToHTMLSource(src, 'ExportError', 'Nothing to export');
return;
end
defaultName = sprintf('plot_%s.png', string(datetime('now', 'Format', 'yyyyMMdd_HHmmss')));
[file, path] = uiputfile({'*.png';'*.jpg';'*.pdf'}, 'Export plot', defaultName);
if isequal(file, 0)
sendEventToHTMLSource(src, 'ExportComplete', 'cancelled');
return;
end
try
exportgraphics(ax, fullfile(path, file), 'Resolution', 300, ...
'BackgroundColor', fig.Color);
sendEventToHTMLSource(src, 'ExportComplete', file);
catch ME
sendEventToHTMLSource(src, 'ExportError', ME.message);
end
Pass fig.Color as BackgroundColor so the export matches the on-screen figure background. Important for dark themes, where the default white background looks wrong.
Implementation Checklist
Before deploying a uihtml app, verify:
HTML file exists in correct location
HTMLSource property set to correct file path
HTMLEventReceivedFcn callback defined
JavaScript setup(htmlComponent) function implemented
Event listeners added for MATLAB→JS communication
Try-catch blocks wrap all MATLAB event handling
Input validation implemented for all user data
Error events sent back to JavaScript for user feedback
CSS styling applied consistently (see matlab-uihtml-design), free of AI-slop tells (purple-blue gradients, side-stripe borders, gradient text, over-rounded cards, generic Arial/Inter)
Contrast verified (body/label ≥4.5:1, large text and UI boundaries ≥3:1)
Keyboard focus is visible (:focus-visible), not removed with a bare outline: none
Responsive design tested at different window sizes
All user interactions provide visual feedback
Loading indicators shown for long operations
File organization follows project structure
Documentation (README) created with usage instructions
Resource cleanup wired to CloseRequestFcn: any timer, listener, or background task is stopped and deleted when the figure closes. Without this, callbacks fire against deleted graphics after the user closes the app.
Long operations are cancellable: if the operation can run more than ~1 second, the user can stop it without killing the figure.
Troubleshooting
Issue: HTML file not loading in uihtml component
Solution: Check file path is absolute or relative to current directory
Solution: Verify HTMLEventReceivedFcn is set before HTML loads
Solution: Check JavaScript is calling sendEventToMATLAB correctly
Issue: Data not updating in JavaScript
Solution: Ensure DataChanged event listener is registered in setup()
Solution: Verify MATLAB is setting h.Data property, not sending event
Issue: JavaScript errors in browser console
Solution: Open browser dev tools (F12) to see detailed error messages
Solution: Ensure htmlComponent is passed to setup() function
Solution: Check for typos in element IDs and function names
Issue: MATLAB errors not displayed to user
Solution: Implement error event handling in both MATLAB and JavaScript
Solution: Use try-catch in MATLAB and send error messages via sendEventToHTMLSource
Issue: Slow performance when sending data
Solution: Reduce frequency of updates (throttle events)
Solution: Send only changed data, not entire datasets
Solution: Use appropriate data types (numbers vs strings)
Issue: Complex data structures not transferring correctly
Solution: Use MATLAB structs (automatically converted to JSON)
Solution: Avoid nested cell arrays; use struct arrays instead
Solution: Test data transfer with simple examples first
Issue: Styling not appearing correctly
Solution: Verify CSS is in <style> block inside <head>
Solution: Check for CSS syntax errors
Solution: Use browser dev tools to inspect computed styles
Additional Resources
MATLAB Documentation: doc uihtml
HTML/CSS/JavaScript: MDN Web Docs
Event handling: doc sendEventToHTMLSource
Figure creation: doc uifigure
Debugging: Use browser Developer Tools (F12)
Related Skills
matlab-uihtml-design: visual design styles (Clean, Material, Cosmic Dark, etc.) with ready-to-use HTML templates and design specs. Use it to style your uihtml controls.