{"id":48630036,"url":"https://github.com/jakobtroidl/web-mlp","last_synced_at":"2026-04-09T04:35:13.599Z","repository":{"id":197909673,"uuid":"690791228","full_name":"jakobtroidl/web-mlp","owner":"jakobtroidl","description":"Fast WebGPU based MLP inference","archived":false,"fork":false,"pushed_at":"2024-03-13T14:55:25.000Z","size":8347,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-04-28T05:15:02.572Z","etag":null,"topics":["deep-learning","inference","machine-learning","mlp","webgpu"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jakobtroidl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-09-12T22:18:15.000Z","updated_at":"2024-03-13T13:51:50.000Z","dependencies_parsed_at":"2023-12-07T21:28:06.469Z","dependency_job_id":"5f52bb48-d360-4d40-9bc0-bb452dca5ae0","html_url":"https://github.com/jakobtroidl/web-mlp","commit_stats":null,"previous_names":["jakobtroidl/web-mlp"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jakobtroidl/web-mlp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakobtroidl%2Fweb-mlp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakobtroidl%2Fweb-mlp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakobtroidl%2Fweb-mlp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakobtroidl%2Fweb-mlp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jakobtroidl","download_url":"https://codeload.github.com/jakobtroidl/web-mlp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakobtroidl%2Fweb-mlp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31586405,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"online","status_checked_at":"2026-04-09T02:00:06.848Z","response_time":112,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["deep-learning","inference","machine-learning","mlp","webgpu"],"created_at":"2026-04-09T04:35:13.450Z","updated_at":"2026-04-09T04:35:13.586Z","avatar_url":"https://github.com/jakobtroidl.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![npm version](https://img.shields.io/npm/v/web-mlp.svg?color=1a8cff)](https://www.npmjs.com/package/web-mlp)\n\n# WebGPU accelerated MLP inference\n\nHardware accelerated inference of multi-layer perceptions (MLPs) in the browser. Works in [browsers supporting WebGPU](https://github.com/gpuweb/gpuweb/wiki/Implementation-Status).\n\n## Install\n\n```\nnpm i web-mlp\n```\n\n## Quick Start\n\n```javascript\nimport {\n    initWebGPU,\n    createMLP,\n    from_json\n} from \"web-mlp\";\n\nasync function testMLP() {\n    let batch_size = 20;\n    let tile_size = 8;\n    const path = \"https://jakobtroidl.github.io/data/mlp-v11.json\"; // path to example model's JSON file\n\n    let device = await initWebGPU();\n    let model_data = await from_json(path); // load model\n    let [model, outputBuffer] = await createMLP(model_data, batch_size, tile_size); // convert to WebMLP model for fast inference\n    let X = Float32Array.from(Array(batch_size * model.inputSize).fill(0), () =\u003e Math.random()); // generate random a input\n\n    let commandEncoder = device.createCommandEncoder();\n\n    let start = performance.now();\n    model.inference(X, commandEncoder); // inference the model\n    device.queue.submit([commandEncoder.finish()]);\n    let result = await model.transferToCPU(outputBuffer);\n    let end = performance.now();\n\n    console.log(\"WebMLP Inference time + Data Transfer: \", end - start, \"ms\");\n    console.log(\"WebMLP result\", result);\n    console.log(\"WebMLP result should match dummy_output in model.json\");\n}\n\ntestMLP();\n```\n\nDepending on your computing hardware, you can increase `batch_size` and `tile_size` . Tested on a MacBook Pro 2021 w/ Intel GPU, which supports up to `batch_size=800000` and `tile_size=32` . Check out [this website](https://webgpureport.org/) to view WebGPU limits for your own device.\n\n## PyTorch to WebMLP\n\nWebMLP is purely designed for model inference. That means you can't use it to train an MLP. We recommend training the MLP in PyTorch and exporting the model to JSON for WebMLP inference. Here, we describe how that process works. \n\n\u003e Note: This approach for loading pretrained weights is currently more of a hack than a thought-through solution. Any ideas on how to make that more robust are welcome. Please open an issue with ideas. \n\n### PyTorch export \n\n```python\nimport torch.nn as nn\nimport torch\nimport json\n\nclass MLP(nn.Module):\n    def __init__(self, in_dim, out_dim, n_hidden, n_neurons):\n        super().__init__()\n        layers = []\n        self.in_dim = in_dim\n        self.out_dim = out_dim\n        lastv = in_dim\n        for i in range(n_hidden):\n            layers.append(nn.Linear(lastv, n_neurons))\n            layers.append(nn.ReLU())\n            lastv = n_neurons\n        layers.append(nn.Linear(lastv, out_dim))\n        self.layers = nn.Sequential(*layers)\n\n    def forward(self, x):\n        shape = x.shape[:-1]\n        x = self.layers(x.view(-1, x.shape[-1]))\n        return x.view(*shape, -1)\n\n    def export(self, filename):\n        # export model\n        self.eval()\n\n        dummy_input = torch.ones(1, self.in_dim).cuda()\n        dummy_out = self(dummy_input)\n\n        activation = \"Relu\"\n        weights_and_biases = {}\n        weights_and_biases['input_shape'] = [None, self.in_dim]\n        weights_and_biases['output_shape'] = [None, self.out_dim]\n        weights_and_biases['activations'] = activation\n        weights_and_biases['dummy_input'] = dummy_input.cpu().detach().numpy().tolist()\n        weights_and_biases['dummy_output'] = dummy_out.cpu().detach().numpy().tolist()\n\n        layers = {}\n        for name, param in self.named_parameters():\n            name_parts = name.split('.')\n            key = name_parts[0] + \".\" + name_parts[1]\n            if key not in layers:\n                layers[key] = {}\n            param_np = param.cpu().detach().numpy()\n            layers[key][name_parts[2]] = param_np.flatten(order=\"F\").tolist()\n            layers[key][name_parts[2] + '_shape'] = list(param_np.shape)\n\n        sorted_keys = sorted(layers.keys())\n        weights_and_biases['layers'] = [layers[key] for key in sorted_keys]\n\n        # safe weights and biases as json\n        with open(filename, 'w') as outfile:\n            json.dump(weights_and_biases, outfile)\n```\n\n### WebMLP Input Format\n\nWebMLP takes a JSON file as input. The file format is described below. [Here's](https://jakobtroidl.github.io/data/mlp-v11.json) an example of a small 3-hidden 64-neuron layer MLP in that file format. \n\n```json5\n{\n    \"input_shape\": [\n        null, 19\n    ],\n    \"output_shape\": [\n        null, 1\n    ],\n    \"activations\": \"Relu\", // applied to all layers except the last layer. Options are [Relu, Sigmoid, Tanh, Linear]\n    \"dummy_input\": [[ /* Example Input */ ]],\n    \"dummy_output\": [[ /* Expected Output for dummy input. Can be used to verify if inference works */ ]]\n    \"layers\": [\n        {\n            \"weight\": [ /* Linear Layer 1 weights as Float32Array. Ordered row-major. */ ],\n            \"weight_shape\": [ 64, 19], /* output dimension, input dimension */\n            \"bias\": [ /* Linear Layer 1 biases as Float32Array. Ordered row-major. */ ],\n            \"bias_shape\": [ 64 ]\n        }, \n        { ... }, // Layer 2\n        { ... }  // Layer N\n    ]\n}\n```\n\n## Development\n\n```sh\ngit clone https://github.com/jakobtroidl/web-mlp.git\ncd web-mlp\nnpm install\nnpm run dev\n```\n\n## Publish to npm\n\n```\n// ensure all changes are comitted\nnpm run release-patch // release new patch version\nnpm run release-minor // release new minor version\nnpm run release-major // release new major version\n```\n\n## License\n\nMIT license ([LICENSE-MIT](LICENSE-MIT)).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakobtroidl%2Fweb-mlp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjakobtroidl%2Fweb-mlp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakobtroidl%2Fweb-mlp/lists"}