A real-time interactive simulation of the solar system.
Quick start
- Clone this repo:
git clone https://github.com/jshor/tycho.git- Use the right node version:
nvm use- Install dependencies (via Yarn):
yarn- Start:
yarn startFramework and libraries
This app is written in TypeScript and built using React.js, THREE.js, and zustand for state. It uses react-three-fiber for THREE-based React components, along with its drei helpers. It's bundled using Vite.
Styling
Each DOM component contains a corresponding Sass stylesheet and employs the BEM CSS design pattern. Vite compiles the Sass as part of the build.
Application Architecture
The overall architecture follows a container pattern, split between src/modules and src/components. In a nutshell, modules in this app leverage tasks such as handling state and minor front-end logic, while components simply handle the presentation. Physics calculations and business logic is delegated to services, and some critical components, such as the Camera, have dedicated services.
State management
All shared-state data is stored in the zustand store, which lives in src/store. This includes the 2D and 3D positions of objects and the current time. Modules subscribe to the slices they need and the store is written to through its actions, which keeps the data flowing in one direction. In rare cases, some components will use the local state for things internal to that component, when appropriate.
File structure
This project utilizes a typical React app flat directory structure. Tests for each item typically live in __tests__. Components have corresponding tests and Sass in the same directory. __tests__ folders may contain __fixtures__.
The source is laid out as follows:
| Path | Contents |
|---|---|
src/modules |
Logical components that hold state and front-end logic |
src/components |
Presentational components with styling |
src/utils |
Functional utilities and helpers, including for math and physics |
src/store |
The Zustand store shared across the app |
src/shaders |
GL fragment and vertex shaders for the scene |
src/elements |
Scene aids, such as the clock, camera controls, and ambience audio |
src/constants |
Tunable values for the scene, the UI, and the tour |
src/test |
Test setup and utilities |
Note that 3D React components and DOMElement ones coexist in the same general paths.
Static assets, such as textures, media, and data are stored in the public folder. Some JSON file contents are generated by build scripts.
Running the project
In the project directory, you can run the following commands:
Runs the app in the development mode.
Open http://localhost:3000 to view it in the browser.
The page will hot reload if you make edits.
Unit testing
Tests use Vitest and Testing Library, running against jsdom. Shared setup lives in src/test, which stubs the browser APIs jsdom lacks and stands in for react-three-fiber, so that scene code can be rendered and asserted on without a WebGL context.
Runs the whole test suite once, as CI does.
Launches the test runner in the interactive watch mode.
Runs all tests and prints out code covfefe.
Linting and formatting
Runs the linter using ESLint, configured in flat format in eslint.config.cjs.
Note that the project compiles with TypeScript 7, while typescript-eslint still requires the TypeScript 6 API. Both are installed side by side for that reason: typescript is the compiler, and the typescript-6 alias exists only so the linter can parse. The config seeds the former with the latter, and both it and the extra dependency can go once typescript-eslint supports TypeScript 7.
Fixes any linting errors discovered by the linter and reports any issues that could not be addressed automatically.
Type checks the project without emitting anything.
Formats the project using Prettier. Use yarn format:check to report on formatting without writing any changes.
Building
Type checks the project and builds the app for production to the dist folder.
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.
Serves the built dist folder locally, to check a production build before it ships.
Maintaining ephemerides
Ephemerides are all of the orbital characteristic data points used to aid in the computation of celestial positions of orbital bodies.
For information on the file structure for these JSON files, please see the Wiki.
The maintainable versions of all orbital data (including their ephemerides) are stored in public/static/data/orbitals/{heliocentric-system}/{satellite-or-barycentric-orbital}.json.
public/static/data/orbitals/earth/earth.jsonmoon.json
yarn ephemerides
Takes all orbital JSONs, probes the JPL Horizons database for ephemerides, and updates each JSON accordingly. For more information on this script, and on the prerequisite data format, please see this wiki page.
The main orbitals.json file in public/static/data is a compilation of all the orbital JSONs. It is a flat array of all the orbitals in the Solar System scene, including all planets and satellites.
yarn ephemerides:build
Compiles the orbitals.json file. This is also run as part of yarn build.
Deployment
Deployment is handled by GitHub Actions, defined in .github/workflows/merge.yml. On every push to main, the workflow lints the project, runs the tests with coverage, reports that coverage to Codecov, and builds the app. The resulting dist folder is then published to the gh-pages branch, along with a CNAME pointing at tycho.io.
- Project developed by Josh Shor
- Planetary ephemerides courtesy of NASA and the Jet Propulsion Laboratory.
- Orbital textures by James Hastings-Trew.
- Ambient music: Ultra Deep Field by Stellardrone.
- Special thanks to the open source community for React.js, THREE.js, and react-three-fiber.
