OONNX.css

About this demo

Neural networks
with style...

...sheets. ONNX.css compiles a fixed-shape ONNX graph into a CSS stylesheet, then runs inference using your browser's style engine.

How it works:

  1. 01
    Decode

    JavaScript reads the ONNX protobuf and resolves fixed tensor shapes, constants, weights, and graph topology.

    model = decode(bytes)
    for (node of model.graph.node)
      css += expand(node)
  2. 02
    Expand

    Each tensor element in the graph becomes a typed CSS custom property. Operators become expressions referencing earlier properties.

    --x0: 0;
    --x1: 0;
    --x2: max(0, calc(var(--x0) + var(--x1)));
  3. 03
    Bind

    Input numbers are written as inline custom properties on a hidden "engine" HTML element.

    <div data-onnx-css-engine
      style="--x0:.7; --x1:-.2"></div>
  4. 04
    Resolve

    getComputedStyle() on the engine element triggers the browser to evaluate the dependency graph. The final properties are the model outputs.

    computed = getComputedStyle(engine)
    logits = ["--x3", "--x4"].map(name =>
      Number(computed.getPropertyValue(name)))

Is this practically useful?

Not really :)

Real inference runtimes are faster, smaller, more precise, better supported, and easier to debug. Compiled CSS can grow to tens of megabytes for a tiny network, and not all ops are supported.

Regardless, it's cool that you can build a reactive numeric dataflow graph from CSS primitives. Once a model is flattened into scalar expressions, CSS functions provide the building blocks for neural network convolutions, activations, pooling, and matrix multiplication without JavaScript executing those operators.

01

Try your model

Fixed-shape .onnx protobuf

Models must use fixed tensor topology. Dynamic control flow, runtime-sized outputs, strings, sequences, and sparse or external tensors are not supported.

02

Compiled CSS

Waiting for a model

The generated stylesheet will appear here.

Current coverage

Supported ONNX operators

Not all ONNX ops are supported, like those that require dynamic control flow.

AbsAddArgMaxArgMinAveragePoolBatchNormalizationCastClipConcatConstantConvDivEluEqualExpExpandFlattenGatherGemmGlobalAveragePoolGreaterGreaterOrEqualHardSigmoidHardSwishInstanceNormalizationLayerNormalizationLeakyReluLogMatMulMaxMaxPoolMinMulIdentityLessLessOrEqualNegNotPadPowReciprocalReduceL1ReduceL2ReduceMaxReduceMeanReduceMinReduceProdReduceSumReluReshapeSigmoidSoftmaxShapeSizeSoftplusSoftsignSqrtSqueezeSubSumTanhTileTransposeUnsqueezeSliceWhere