{"id":14384818,"url":"https://github.com/madjam002/specular","last_synced_at":"2026-03-06T11:05:01.417Z","repository":{"id":57141840,"uuid":"46752347","full_name":"madjam002/specular","owner":"madjam002","description":"Build lightshows with React","archived":false,"fork":false,"pushed_at":"2016-06-18T14:35:15.000Z","size":118,"stargazers_count":19,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-10-05T23:54:51.640Z","etag":null,"topics":[],"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/madjam002.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}},"created_at":"2015-11-23T22:38:36.000Z","updated_at":"2023-09-19T13:41:34.000Z","dependencies_parsed_at":"2022-09-05T07:50:29.712Z","dependency_job_id":null,"html_url":"https://github.com/madjam002/specular","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/madjam002/specular","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/madjam002%2Fspecular","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/madjam002%2Fspecular/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/madjam002%2Fspecular/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/madjam002%2Fspecular/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/madjam002","download_url":"https://codeload.github.com/madjam002/specular/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/madjam002%2Fspecular/sbom","scorecard":{"id":610395,"data":{"date":"2025-08-11","repo":{"name":"github.com/madjam002/specular","commit":"fc05245ee77e3ff26f90d93db7f7056c0bd86ba7"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.8,"checks":[{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: npmCommand not pinned by hash: scripts/bootstrap.sh:6","Info:   0 out of   1 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-21T02:30:41.317Z","repository_id":57141840,"created_at":"2025-08-21T02:30:41.317Z","updated_at":"2025-08-21T02:30:41.317Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30173376,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T07:56:45.623Z","status":"ssl_error","status_checked_at":"2026-03-06T07:55:55.621Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":[],"created_at":"2024-08-28T18:01:41.819Z","updated_at":"2026-03-06T11:04:56.406Z","avatar_url":"https://github.com/madjam002.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"specular [![Build Status](https://img.shields.io/travis/madjam002/specular.svg?style=flat)](https://travis-ci.org/madjam002/specular) [![Downloads](https://img.shields.io/npm/dm/specular.svg)](https://npmjs.com/package/specular) [![NPM](https://img.shields.io/npm/v/specular.svg)](https://npmjs.com/package/specular)\n========\n\n*This project is a work in progress, website coming soon! It has been released early to get feedback.*\n\n\u003e \u003cfont size=\"4\"\u003e Custom renderers for [React](https://github.com/facebook/react). Build light shows, control devices, and more!\u003cfont\u003e\n\nSpecular is a platform which allows you to use [React](https://github.com/facebook/react) to render to targets other than the DOM.\nCan run in NodeJS, or alongside React in the browser (depending on the renderer you use).\nWhilst Specular started off as a library to control physical hardware, it can be used for anything, including your web based adventures!\n\nThe core module itself (`specular`) which provides the platform is very lightweight and can be used for doing any kind of custom rendering\nthat isn't mentioned here e.g rendering to WebGL or even OpenGL using NodeJS bindings, controlling and listening to serial interfaces,\nand anything else your mind can think of!\n\nThere are several pre-made renderers available today which are part of this repository.\n\n---\n\n#### specular-dmx\n\u0026check; Browser (partial) \u0026check; NodeJS\n\nAllows you to build light shows using React and output them to real light fixtures using DMX and Artnet*.\n\n*docs coming soon*\n\n\u003csmall\u003e* ArtNet output is only supported in NodeJS. You can use `specular-visualiser` (not yet released) to visualise\nDMX output in the browser using WebGL.\u003c/small\u003e\n\n---\n\n#### specular-midi\n\u0026cross; Browser \u0026check; NodeJS\n\nControl and interact with MIDI devices like the Novation Launchpad and the APC Mini. Can also be used to control MIDI keyboards and other instruments.\nUses the `midi` npm module, meaning it only runs on NodeJS.\n\n*docs coming soon*\n\n---\n\n*More coming soon...*\n\n---\n\n## Getting started\n\n```sh\n$ npm install react specular --save\n```\n\nCongratulations, you've just opened up a whole world of possibilities. :tada: :tada: :tada:\n\nNow you can follow one of the getting started guides below:\n\n- [A step by step guide on building a custom renderer (using canvas as an example)](docs/building-a-renderer.md)\n- *Build a light show using specular-dmx (coming soon)*\n- *Control MIDI devices with specular-midi (coming soon)*\n\n### Barebones example to get started\n\n```javascript\nimport React from 'react'\nimport Specular, {Scene} from 'specular'\n\nconst renderTargets = []\n\nSpecular.render(\n  \u003cScene\u003e\n    {/* Components will go here */}\n  \u003c/Scene\u003e\n, renderTargets)\n```\n\nThere's a few things to note here.\n\n- The `\u003cScene\u003e` component is the `\u003cdiv\u003e` of the Specular world. It is simply a container element which allows you to render children.\nUnlike a `\u003cdiv\u003e` however, it doesn't effect the actual render output.\nIf you render a component inside or outside of a `\u003cScene\u003e`, it will make no difference, it is simply provided as a container component.\n\n- `Specular.render()` takes a tree of elements like `ReactDOM.render`, but unlike `ReactDOM` it doesn't take a DOM node to mount to. Instead you provide a list of render targets as the second argument (see below).\n\n\n### Renderers (or render targets)\n\nSpecular allows you to render the same React tree to multiple render targets. This could be useful if it made sense to combine components which output to different renderers. For example, you might want to use `specular-dmx` to build a light show to output to DMX lighting fixtures, but at the same time use `specular-midi` to output to devices which are controlled via MIDI.\n\nA renderer can be thought of as a render pass.\nEvery time a component in your React tree updates, it triggers Specular to re-render.\n\nSpecular then goes through the render targets that you provided (in the same order), and passes the React tree to the renderer for it to work its magic.\n\nA renderer passed to `Specular.render` is simply an object which has some different render pass hooks to do its processing on. The hooks available are:\n\n#### `before`\nCalled before iterating through the React tree.\nCan be useful for \"preparing\" to render to whatever you're rendering to. In the case of canvas, it'll be used to clear the canvas context so we can start to draw stuff to it.\n\n#### `render(ReactComponent)`\nGets called for each React Component in your React tree. This is useful for actually rendering something for each component. It's called in the same order that your components are rendered in the React tree.\n\n#### `after`\nCalled after rendering each component, useful for cleaning up or finishing the render process.\n\nThe renderers provided in this repository all implement these hooks appropriately. You can see the source code for more details on how it works.\n\n### Using a renderer\n\nIn `index.js`:\n\n```javascript\nimport React from 'react'\nimport Specular, {Scene} from 'specular'\nimport SpecularDMX from 'specular-dmx'\nimport ArtNet from 'specular-dmx-artnet'\n\nconst renderTargets = [\n  SpecularDMX({\n    /* Config for SpecularDMX, sets DMX universe 0 to output to ArtNet */\n    0: ArtNet('127.0.0.1')\n  })\n]\n\nSpecular.render(\n  \u003cScene\u003e\n    {/* Components will go here */}\n  \u003c/Scene\u003e\n, renderTargets)\n```\n\n## License\n\nLicensed under the MIT License.\n\nView the full license [here](https://raw.githubusercontent.com/madjam002/specular/master/LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmadjam002%2Fspecular","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmadjam002%2Fspecular","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmadjam002%2Fspecular/lists"}