NNotepad is a browser-based playground for experimenting with WebNN expressions without boilerplate code. As of mid-2024, WebNN is available as a prototype in Chromium-based browsers, but requires launching the browser with particular flags enabled.
Type assignments like foo = 1 + 2
or expressions like 2 * foo
. The result of the last assignment or expression is shown. Some examples:
1 + 2
# yields 3
a = 123
b = 456
a / b
# yields 0.2697368562221527
A = [[1,7],[2,4]]
B = [[3,3],[5,2]]
matmul(A,B)
# yields [[38,17],[26,14]]
NNotepad translates what you type into script that builds a WebNN graph, evaluates the script, then executes the graph. Click 🔎 to see the generated script.
Expressions can use:
- Operators
+
,-
,*
,/
,^
,==
,<
,<=
,>
,>=
,!
with precedence, and(
,)
for grouping. - Function calls like
add()
,matmul()
,sigmoid()
, and so on. - Numbers like
-12.34
. - Tensors like
[[1,2],[3,4]]
. - Dictionaries like
{alpha: 2, beta: 3}
, arrays like[ A, B ]
, strings like"float32"
, and booleanstrue
andfalse
.
Functions and operators are turned into MLGraphBuilder
method calls.
Array literals ([...]
) and number literals (12.34
) are interpreted contextually:
- In assignments, they are intepreted as tensor/scalar constant
MLOperand
s, e.g.alpha = 12.34
orT = [1,2,3,4]
. - In most function calls, they are interpreted as tensor/scalar constant
MLOperand
s, e.g.neg(123)
orneg([1,2,3])
. - In some function calls, they are interpreted as arrays/numbers for some positional parameters, e.g.
concat([A,B,C],0)
. This includes:concat()
,expand()
,pad()
,reshape()
,slice()
,split()
. - In dictionaries, they are interpreted as arrays/numbers, e.g.
linear(123, {alpha: 456, beta: 789})
ortranspose(T, {permutation: [0,2,1]})
. To pass a tensor/scalar constant in a dictionary, use a variable or wrap it inidentity()
e.g.gemm(A, B, {c:identity([4])})
orgemm(A, B, {c:identity(4)})
.
The default data type for scalars and tensors is float32
. To specify a different data type, suffix with one of i8
, u8
, i32
, u32
, i64
, u64
, f16
, f32
, e.g. 123i8
or [1,2,3]u32
.
In addition to WebNN MLGraphBuilder
methods, you can use these helpers:
- load(url, shape, dataType) - fetch a tensor resource. Must be served with appropriate CORS headers. Example:
load('https://www.random.org/cgi-bin/randbyte?nbytes=256', [16, 16], 'uint8')
- zeros(shape, dataType) - constant zero-filled tensor of the given shape. Example:
zeros([2,2,2,2], 'int8')
float16
support (and thef16
suffix) is experimental.- Whitespace including line breaks is ignored.
- Parsing around the "unary minus" operator can be surprising. Wrap expressions e.g.
(-a)
if you get unexpected errors. - If output is a constant, it will be wrapped with
identity()
if your back-end supports it. Otherwise, you must introduce a supported expression.
What ops are supported, and with what data types, depends entirely on your browser's WebNN implementation. Here be dragons!
Anything after # or // on a line is ignored (outside other tokens)
{} means 0-or-more repetitions
[] means 0-or-1 repetitions
() for grouping
| separates options
'' is literal
// is regex
program = line { line }
line = assigment | expr
assigment = identifier '=' expr
expr = relexpr
relexpr = addexpr { ( '==' | '<' | '<=' | '>' | '>=' ) addexpr }
addexpr = mulexpr { ( '+' | '-' ) mulexpr }
mulexpr = powexpr { ( '*' | '/' ) powexpr }
powexpr = unyexpr { '^' unyexpr }
unyexpr = ( '-' | '!' ) unyexpr
| finexpr
finexpr = number [ suffix ]
| array [ suffix ]
| string
| boolean
| dict
| identifier [ '(' [ expr { ',' expr } ] ')' ]
| '(' expr ')'
string = /("([^\\\x0A\x0D"]|\\.)*"|'([^\\\x0A\x0D']|\\.)*')/
number = /NaN|Infinity|-Infinity|-?\d+(\.\d+)?([eE]-?\d+)?/
boolean = 'true' | 'false'
identifier = /[A-Za-z]\w*/
suffix = 'u8' | 'u32' | 'i8' | 'i32' | 'u64' | 'i64' | 'f16' | 'f32'
array = '[' [ expr { ',' expr } ] ']'
dict = '{' [ propdef { ',' propdef } [ ',' ] ] '}'
propdef = ( identifier | string ) ':' expr