Mind Reading With
Intelligent & Adaptive UIs
@davidkpiano
Everyone is
different.
A/B tests
are ineffective.
User flows are
seldom linear.
๐
๐
๐
๐
Adaptive user interfaces (AUI)
Adapt to people,
not just devices
๐จ Inbox
๐ VIEW LATEST
โ๏ธ New Email
๐พ Save as Draft
๐ฌ Send
100%
40%
60%
10%
90%
Delete
Are you sure?
Yes
No
Delete
Undo
Deleted!
๐ฉโ๐ผ
๐โโ๏ธ
๐โโ๏ธ
Developing AUIs
is challenging.
Model-driven development
Finite state machines
-
have one initial state
-
a finite number of states
-
a finite number of events
-
a mapping of state transitionsย
triggered by events -
a finite number of final states
Sign in
Signing in...
Profile
Home
SIGN IN
SUCCESS
UNAUTHORIZED
PROFILE
Signed Out
SIGN OUT
Signed Out
Define transitions between
states & events
function searchReducer(state = 'idle', event) {
switch (state) {
case 'idle':
switch (event.type) {
case 'SEARCH':
return 'searching';
default:
return state;
}
case 'searching':
switch (event.type) {
case 'RESOLVE':
return 'success';
case 'REJECT':
return 'failure';
default:
return state;
}
case 'success':
switch (event.type) {
case 'SEARCH':
return 'searching';
default:
return state;
}
case 'failure':
switch (event.type) {
case 'SEARCH':
return 'searching';
default:
return state;
}
default:
return state;
}
}
FSMs with switch/case
const machine = {
id: 'feedback',
initial: 'question',
states: {
question: {
on: {
CLICK_GOOD: 'thanks',
CLICK_BAD: 'form',
CLOSE: 'closed',
ESC: 'closed'
}
},
form: {
on: {
SUBMIT: 'thanks',
CLOSE: 'closed',
ESC: 'closed'
}
},
thanks: {
on: {
CLOSE: 'closed',
ESC: 'closed'
}
},
closed: {
type: 'final'
}
}
};
Define transitions between
states & events
const transition = (state, event) => {
return machine
.states[state] // current state
.on[event] // next state
|| state; // or same state
}
FSMs with object mapping
import { Machine } from 'xstate';
const feedbackMachine = Machine({
id: 'feedback',
initial: 'question',
states: {
question: {
on: {
CLICK_GOOD: 'thanks',
CLICK_BAD: 'form',
CLOSE: 'closed',
ESC: 'closed'
}
},
form: {
on: {
SUBMIT: 'thanks',
CLOSE: 'closed',
ESC: 'closed'
}
},
thanks: {
on: {
CLOSE: 'closed',
ESC: 'closed'
}
},
closed: {
type: 'final'
}
}
});
npm install xstate
import { feedbackMachine } from './feedbackMachine';
import { useMachine } from '@xstate/react';
const App = () => {
// const [state, dispatch] = useReducer(feedbackReducer, 'question');
const [current, send] = useMachine(feedbackMachine);
if (current.matches('question')) {
return (
<QuestionScreen
onClickGood={() => send({ type: 'GOOD' })}
onClickBad={() => send({ type: 'BAD' })}
onClose={() => send({ type: 'CLOSE' })}
/>
);
} else if (/* ... */) {
// ...
}
}
Statecharts
(Hierarchical State Machines)
Statecharts
Idle
Searching...
entry / prefetchResources
entry / fetchResults
Search
- Actionsย - onEntry, onExit, transition
- Guardsย - conditional transitions
[query.length > 0]
Statecharts
- Actionsย - onEntry, onExit, transition
- Guardsย - conditional transitions
- Hierarchyย - nested states
- Orthogonalityย - parallel states
- Historyย - remembered states
H
Idle
Searching...
SEARCH
Success
Failure
RESOLVE
REJECT
SEARCH
Searched
Hierarchical states
Idle
Searching...
SEARCH
Success
Failure
RESOLVE
REJECT
SEARCH
Searched
Hierarchical states
const machine = Machine({
initial: 'idle',
states: {
// ...
searching: {
on: {
RESOLVE: 'searched',
REJECT: 'searched.failure'
}
},
searched: {
initial: 'success',
states: {
success: {},
failure: {}
},
on: { SEARCH: 'searching' }
}
}
});
How was your experience?
๐
Good
Bad
Tell me why?
๐
Submit
Thanks for your feedback.
๐
Close
Ain't nothin' but a heartache
- Abstract model
- Transition analytics
- Identify adaptive paths
- Use analysis for adaptation
Game plan
Photo by NEW DATA SERVICESย on Unsplash
Success
Signing in
Error
0.9
0.1
Weighted graphs
State machines inย
VS Live Share
Signed out
Signing in
Signed in
SIGN IN
SIGN IN SUCCESS
SIGN IN FAILURE
Sharing...
Shared
Joining...
Joined
share
share Success
Join Success
Join
Leave
End Collab session
Sign in
How was your experience?
๐
Good
Bad
Tell me why?
๐
Submit
Thanks for your feedback.
๐
Close
Give Feedback
โ
โ
Ain't nothin' but a mistake
Login
Gallery
Profile
Camera
SUCCESS
TAP PROFILE
TAP CAMERA
BACK
BACK
Scroll
Analysis of transitions
Login, Gallery, Gallery
Login, Gallery, Camera
Login, Gallery, Profile
0.90, 0.31
0.90, 0.57
0.90, 0.12
0.90
0.31
0.57
0.13
UNAUTHORIZED
0.10
SUCCESS
(decide)
TAP AD
Analysis of events
+10
Gallery
Ads interspersed
Ads shown at top
Ad
Scroll
Scroll
TAP AD
+10
-1
-10
INTERSPERSED > .5
TOP > .5
Decision trees
Contextual data
- Registration date
- Number of friends
- Posting frequency
- Location
- ...etc.
New user?
< 30 days
>= 30 days
Show ad at top
Friend count?
< 100
>= 100
Show more ads
Show less ads
All data
13%
8%
79%
Prediction via
shortest paths
A
B
C
D
E
- Shortest pathย algorithms (Dijkstra, Bellman-Ford, A* search, etc.)
- Analyticsย provides weights
- Represents happy paths
- Can be automatically generated
A
B
C
D
E
import { Machine } from 'xstate';
const myMachine = Machine({
// ...
});
const myService = interpret(myMachine)
.onTransition(state => {
analytics.track({
target: state.value,
source: state.history?.value,
event: sanitize(event),
timestamp: Date.now()
});
})
.start();
Source
Target
Event
A
B
C
D
E
import { Machine } from 'xstate';
import { getShortestPaths } from '@xstate/graph';
const myMachine = Machine({
// ...
});
const shortestPaths = getShortestPaths(myMachine);
-
A: A
-
B: A -> B
-
C: A -> B -> C
-
D: A -> D
-
E: A -> D -> E
A
B
C
D
E
import { Machine } from 'xstate';
import { getShortestPaths } from '@xstate/graph';
const myMachine = Machine({
// ...
});
const shortestPaths = getShortestPaths(myMachine);
0.9
1.0
0.4
0.6
0.1
0.4
0.6
-
A: A
-
B: A -> D -> E -> B
-
C: A -> D -> E -> C
-
D: A -> D
-
E: A -> D -> E
A
B
C
0.75
0.25
D
E
(D) 0.95
(D) 0.05
A -> B A -> C A -> B A -> B
E -> D -> E -> F ->
Higher-order Markov models
Deep Reinforcement
Learning
-
-
+
+
+
Agent
Environment
Action
State
Reward
Deep Reinforcement Learning
service.onTransition(state => {
analytics.track({
source: state.history.value,
target: state.value,
context: state.context,
event: state.event,
timestamp: Date.now()
})
});
Application
Executable Model
Metrics Tracking
Adaptive Transitions
Adaptive UI
โก๏ธ Determinism
๐ Visualization
๐ฉโ๐ป DEV Experience
๐ค Communication
๐ Analytics
๐ฉโ๐ฌ Simulation
๐ฌ Testability
๐จโ๐ฉโ๐งโ๐ฆ User Experience
๐ Prevent Information Overload
๐ก Provide intuitive guidance
โ Understand Tasks & Goals
โ๏ธ Create Personalized Systems
Adaptive User Interfaces
๐ Micro vs. Macro Adaptivity
โ๏ธ Prediction vs. Feedback
๐ Local vs. Global
๐ Data vs. Simulation
Modeling considerations
Advantages
of using statecharts
- Visualized modeling
- Precise diโagrams
- Automatic code generation
- Comprehensive test coverage
- Accommodation of late-breaking requirements changes
xstate
@xstate/react
@xstate/graph
@xstate/vue
@xstate/test
@xstate/analytics
@xstate/sim
@xstate/viz
โ
โ
โ
๐
๐
๐
๐
โ
The world of statecharts
Spectrum community
XState Docs
Make your code do more.
Thank you Nordic.JS!
๐ฎ
Mind Reading with Intelligent & Adaptive UIs
By David Khourshid
Mind Reading with Intelligent & Adaptive UIs
Nordic JS 2019
- 4,556