ReadStar
ModrinthA mod adds configurable and more realistic stars.
Version 1.21.1 is no longer updated.
ReadStar — NeoForge 26.1 Astronomy Mod

A Minecraft sky mod driven by real celestial mechanics and star catalog data, featuring planetary orbital systems, star catalogs, FOV-aware rendering, and server-synchronized celestial configuration.
Author: FrozenStream
Features
- Real Star Catalog — 12,000+ real stars from Gaia DR3 (color) + BSC5 (bright star fallback), with per-star brightness and glow
- Real Celestial Mechanics — Keplerian orbital system with unlimited nesting depth
- Atmospheric Scattering — HSV-based atmosphere color & glow computation with full-sky overlay and stellar halo
- Comet Rendering — Bézier dust + ion dual-tail structure with dynamic wobble
- FOV-Aware Rendering — Custom shader keeps star screen size constant across FOV changes
- Zero-Code Customization — Add 8 moon phases to any body by simply placing PNGs
- Server Sync — Celestial config managed server-side, auto-synced to all clients
Quick Start
./gradlew build # Build
./gradlew runClient # Run client
./gradlew runData # Data generation (atlas config)
./gradlew runServer # Run server
The celestial system is defined via data pack at data/readstar/celestial/system.json, and the star catalog via resource pack at assets/readstar/custom/stars/stars.json.
Config changes take effect with F3+T. Data pack changes require /reload or restart.
Configuration
Config file: run/config/readstar-common.toml
| Option | Default | Description |
|--------|---------|-------------|
| starCoreSize | 0.648 | Star core quad size multiplier |
| starGlowSize | 1.5 | Bright star glow quad size multiplier |
| starFovCompensationStrength | 0.8 | Star size FOV compensation. 1.0 = full, 0.0 = none |
| starFovBrightnessStrength | 1.0 | Star brightness boost when zoomed in. 0.0 = none |
| celestialApparentSizeFactor | 4000.0 | Celestial body apparent size factor |
| celestialApparentSizeMin | 1.024 | Minimum apparent size clamp |
FOV-Aware Rendering
Stars use a custom shader that separates each point into position and offset:
| Attribute | Meaning | Behavior |
|-----------|---------|----------|
| Position (vec3) | Celestial sphere coordinate, shared by 4 vertices | Transformed by MVP, naturally affected by FOV |
| Offset (vec3) | Billboard corner offset, unique per vertex | Multiplied by FovCompensation for size correction |
Formula: FovCompensation = tan(fov/2) / tan(35°) × strength + (1 - strength)
Shader: worldPos = Position + Offset × FovCompensation
Per-Star Brightness
Based on Vmag with independent decay thresholds:
| Parameter | Formula | Notes |
|-----------|---------|-------|
| Alpha | clamp(1 - max(0, Vmag-3)/12, 0.4, 1) | Full brightness at Vmag ≤ 3 |
| RGB | clamp(1 - max(0, Vmag-1)/19, 0.7, 1) | Full brightness at Vmag ≤ 1 |
| Size | clamp(1 - Vmag/12, 0.5, 1) × starCoreSize | Larger Vmag = smaller dot |
Glow quads only for Vmag < 2.0: < 0.5 → high / < 1.5 → medium / < 2.0 → low.
Data Pack — Celestial System
Path: data/readstar/celestial/system.json
Place at saves/<world>/datapacks/<pack>/data/readstar/celestial/system.json or server world/datapacks/.
Structure
{
"System": {
"<name>": {
"mass": <double>,
"radius": <double>,
"luminance": <int>,
"unstableDirtySnowball": <bool>,
"hasAtmosphere": <bool>,
"atmosphereHSV": <int>,
"starHSV": <int>,
"axis": [<x>, <y>, <z>],
"orbit": {
"semiMajorAxis": <double>,
"eccentricity": <double>,
"inclination": <double>,
"argumentOfPeriapsis": <double>,
"longitudeOfAscendingNode": <double>,
"initialMeanAnomaly": <double>
},
"children": { "<name>": { ... } }
}
}
}
Field Reference
| Field | Description |
|-------|-------------|
| mass | Mass (kg). 0 = fixed at parent position |
| radius | Radius (m). Apparent size = max(1.024, radius / distance × factor) |
| luminance | Self-luminosity 015. >0 = star; children auto-resolve 255) |hostStar upward |
| unstableDirtySnowball | Comet flag. true renders dual-tail structure |
| hasAtmosphere | Has atmosphere (controls overlay and glow rendering) |
| atmosphereHSV | Atmosphere HSV color, packed int (H<<16 | S<<8 | V). H=hue, S=saturation, V=density (0
| starHSV | Body's own color, same encoding. Luminous = emission, non-luminous = surface reflection |
| axis | Rotation axis. Zero vector defaults to (0,0,-1) |
| orbit.semiMajorAxis | Semi-major axis (m). 0 = no orbit |
| orbit.eccentricity | Eccentricity. 0 = circular |
| orbit.inclination | Orbital inclination (radians) |
| orbit.argumentOfPeriapsis | Argument of periapsis (radians) |
| orbit.longitudeOfAscendingNode | Longitude of ascending node (radians) |
| orbit.initialMeanAnomaly | Initial mean anomaly (radians) |
Names are case-insensitive. children: {} = no children. Nesting depth is unlimited.
Complete Example
Sun → Earth + Mars → Moon. Real solar system data.
{
"System": {
"Sun": {
"mass": 1.989e30, "radius": 6.957e8, "luminance": 15,
"unstableDirtySnowball": false, "hasAtmosphere": false, "atmosphereHSV": 0, "starHSV": 2172671,
"axis": [0, 0, 0],
"orbit": { "semiMajorAxis": 0, "eccentricity": 0, "inclination": 0, "argumentOfPeriapsis": 0, "longitudeOfAscendingNode": 0, "initialMeanAnomaly": 0 },
"children": {
"Earth": {
"mass": 5.972e24, "radius": 6.371e6, "luminance": 0,
"unstableDirtySnowball": false, "hasAtmosphere": true, "atmosphereHSV": 9738751, "starHSV": 9732275,
"axis": [0, 0, 0],
"orbit": { "semiMajorAxis": 1.496e11, "eccentricity": 0.0167, "inclination": 0, "argumentOfPeriapsis": 1.796, "longitudeOfAscendingNode": 0, "initialMeanAnomaly": 6.240 },
"children": {
"Moon": {
"mass": 7.342e22, "radius": 1.737e6, "luminance": 0,
"unstableDirtySnowball": false, "hasAtmosphere": false, "atmosphereHSV": 0, "starHSV": 1707468,
"axis": [0, 0, 0.5],
"orbit": { "semiMajorAxis": 3.844e8, "eccentricity": 0.0549, "inclination": 0.0899, "argumentOfPeriapsis": 0, "longitudeOfAscendingNode": 0, "initialMeanAnomaly": 0 },
"children": {}
}
}
},
"Mars": {
"mass": 6.417e23, "radius": 3.390e6, "luminance": 0,
"unstableDirtySnowball": false, "hasAtmosphere": true, "atmosphereHSV": 1336835, "starHSV": 1356697,
"axis": [0, 0, 0],
"orbit": { "semiMajorAxis": 2.279e11, "eccentricity": 0.0934, "inclination": 0.0323, "argumentOfPeriapsis": 0, "longitudeOfAscendingNode": 0.865, "initialMeanAnomaly": 0 },
"children": {}
}
}
}
}
}
Inheritance & Time
hostStar: resolved recursively upward to nearestluminance > 0ancestor- Position =
parent.position + orbit(parent.mass, gameTime) - Root fixed at
(0, 0, 0) gameTimedrives orbital motion;daylightTime(0~24000) drives rotation/zenith- Moon phases auto-computed from observer-satellite-star geometry
Atmosphere System
Each body can define atmosphere color (HSV) and its own color (HSV), both as packed ints:
atmosphereHSV / starHSV = (H << 16) | (S << 8) | V
| Component | Bits | Range | Meaning |
|-----------|------|-------|---------|
| H (Hue) | 16-23 | 0255 | Hue (0=red → 255 wraps to red) |255 | Saturation (0=gray, 255=pure color) |
| S (Saturation) | 8-15 | 0
| V (Value) | 0-7 | 0~255 | Density/brightness (atmosphereHSV=density, starHSV=brightness) |
Example values:
| Body | atmosphereHSV | starHSV | Notes |
|------|:--:|:--:|-------|
| Sun | 0 | 2172671 | No atmosphere, G-type yellow-white |
| Earth | 9738751 | 9732275 | Blue sky (H=0.58, S=0.6, V=1.0) |
| Mars | 1336835 | 1356697 | Faint red-brown (V=0.01, very thin) |
Pack/unpack via CelestialBody.packHSV(h, s, v) and getHueFloat/getSaturationFloat/getValueFloat.
Glow computation (computeGlowColor): halo color = starlight × atmospheric scattering. Hue shifts toward atmosphere; saturation and brightness slightly boosted.
Rendering layers:
1. renderSkyDisc → vanilla biome sky background
2. Bodies + stars → luminous / non-luminous + star catalog
3. renderAtmosphereOverlay → translucent full-sky atmosphere (fades at night)
Celestial Textures
Body textures are organized by category, auto-discovered by celestial.json atlas:
assets/<namespace>/textures/environment/celestial/
├── luminous/<name>.png # Luminous bodies (single image, no phases)
├── non-luminous/<name>/ # Non-luminous bodies (8 moon phase PNGs)
└── halo.png # Glow texture (grayscale radial falloff)
Luminous bodies need one PNG in luminous/. Non-luminous bodies need 8 moon phase PNGs in non-luminous/<name>/. readstar:luminous/white_sun.png is a placeholder — replace with your own.
Resource Pack
Star Catalog
Path: assets/readstar/custom/stars/stars.json
Data Sources
Star catalogs are auto-generated by scripts in .data/:
| Script | Source | Output |
|--------|--------|--------|
| generate_stars.py | BSC5 Bright Star Catalogue | stars_named.json (361 IAU-named) + stars_numbered.json (8043 HR-numbered) |
| gaia_download.py | Gaia Archive TAP API | gaia_bright_with_teff.vot (with effective temperature) |
| gaia_to_stars.py | Gaia DR3 + BSC5 fallback | stars_gaia_named.json (361) + stars_gaia_numbered.json (11809) |
Color pipeline: Prefer Gaia GSP-Phot effective temperature → Planckian blackbody → sRGB; fallback to bp_rp color-index mapping. 12 brightest stars (Sirius, Vega, etc.) retain BSC5 data due to Gaia detector saturation.
JSON Format
{
"Stars": [
{
"name": "Sirius",
"position": [-0.1875, -0.2876, 0.9392],
"Vmag": -1.46,
"color": 4294967295
}
]
}
| Field | Description |
|-------|-------------|
| name | Identifier (reserved, no runtime effect) |
| position | Unit sphere direction [x,y,z], normalized to distance 100. Y=North Celestial Pole |
| Vmag | Apparent magnitude (Gaia G-band or BSC5 V-band). Determines glow tier and brightness decay |
| color | ARGB color value; key for atlas sprite generation |
Custom Moon Textures
Core feature: add 8 moon phases to any celestial body by simply placing PNG files — zero code.
Directory
assets/<namespace>/textures/environment/celestial/non-luminous/<body-name>/
<body-name>must matchsystem.jsonname in lowercase- Requires exactly 8 PNGs with fixed filenames:
| File | Phase |
|------|-------|
| full_moon.png | Full |
| waning_gibbous.png | Waning Gibbous |
| third_quarter.png | Third Quarter |
| waning_crescent.png | Waning Crescent |
| new_moon.png | New |
| waxing_crescent.png | Waxing Crescent |
| first_quarter.png | First Quarter |
| waxing_gibbous.png | Waxing Gibbous |
Requirements
| Spec | Value |
|------|-------|
| Format | PNG, RGBA 32-bit |
| Size | 16×16 or 32×32 recommended |
| Edges | Transparent (alpha=0) |
| Color | Self-colored; no runtime tinting |
Example: Jupiter
assets/readstar/textures/environment/celestial/non-luminous/jupiter/
├── full_moon.png
├── waning_gibbous.png
├── third_quarter.png
├── waning_crescent.png
├── new_moon.png
├── waxing_crescent.png
├── first_quarter.png
└── waxing_gibbous.png
Also define Jupiter's orbit in system.json (see example above).
How It Works
- PNGs under
textures/environment/celestial/auto-discovered by Minecraft's atlas system ReadstarSkyRendererscansmoons/subdirectories and builds GPU buffers by group- Runtime matching by
body.name
Other mods can override textures via resource pack priority.
Sprites & Atlas
Star Atlas
assets/readstar/atlases/star.json declares readstar:star source. At runtime, all color values from stars.json tint base stencils per-pixel:
| Stencil | Path | Purpose |
|---------|------|---------|
| star_base.png | textures/environment/star/ | Core (32×32 RGBA) |
| star_glow_low.png | same | Low glow |
| star_glow_med.png | same | Medium glow |
| star_glow_high.png | same | High glow |
Each color → 4 sprites: color_{c}, glow_low_{c}, glow_med_{c}, glow_high_{c}.
Requirements: RGBA 32-bit, 32×32, black edges, glow brightness suppressed ×0.35.
Celestial Atlas
assets/readstar/atlases/celestial.json uses minecraft:directory source, auto-scanning all namespaces under textures/environment/celestial/.
Network Sync
Server loads data/readstar/celestial/*.json and broadcasts via readstar:planet_system packet. Client rebuilds the celestial tree in CelestialBodyManager.initializeFromJson().
Build & Troubleshooting
./gradlew build # Build JAR
./gradlew runData # Data generation (atlas config)
./gradlew runClient # Run client
./gradlew runServer # Run server
Output: build/libs/readstar-*.jar
| Symptom | Check |
|---------|-------|
| Purple/black star textures | Is star_base.png 32-bit RGBA? |
| Purple moon phase | Missing PNG or filename mismatch |
| Body not visible | Does it have hostStar (ancestor luminance>0)? |
| Glow/halo not rendering | Is halo.png under textures/environment/celestial/? |
| Atmosphere color wrong | Are atmosphereHSV / starHSV packed correctly? |
| Data pack not applied | /reload or restart |
| Resource pack not updating | F3+T |
| FOV compensation not working | starFovCompensationStrength > 0? |
© 2026 FrozenStream. Licensed under MIT.
Versions
No version history available. Use the Download button to get the latest from the source.

Comments 0
No comments yet. Be the first to share your thoughts.