Skip to content

ReadStar

A mod adds configurable and more realistic stars.

NeoForge
Loaders
NeoForge
Downloads on Modrinth
2.9K
Followers on Modrinth
8
versions
33
Updated
July 16, 2026Jul 16
Published
in 2025
This is a library — other mods need it, and it adds nothing to the game by itself
The Milky Way band is a long river spanning the entire sky · swipe

At a glance

A library: other mods need it
Runs on NeoForge
For Minecraft 1.21.1, 26.1.1–26.2
Needed in the game and on the server
Latest version — 0.3.7_beta_neoforge_mc1.21.1 from July 16, 2026
License MIT — free
Where it's needed
In gamerequiredOn serverrequired

Needed both in the game and on the server

Game versions1.21.1, 26.1.1–26.2

ReadStar — NeoForge Astronomy Mod

Night

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

OptionDefaultDescription
starCoreSize0.648Star core quad size multiplier
starGlowSize1.5Bright star glow quad size multiplier
starFovCompensationStrength0.8Star size FOV compensation. 1.0 = full, 0.0 = none
starFovBrightnessStrength1.0Star brightness boost when zoomed in. 0.0 = none
celestialApparentSizeFactor4000.0Celestial body apparent size factor
celestialApparentSizeMin1.024Minimum apparent size clamp

FOV-Aware Rendering

Stars use a custom shader that separates each point into position and offset:

AttributeMeaningBehavior
Position (vec3)Celestial sphere coordinate, shared by 4 verticesTransformed by MVP, naturally affected by FOV
Offset (vec3)Billboard corner offset, unique per vertexMultiplied 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:

ParameterFormulaNotes
Alphaclamp(1 - max(0, Vmag-3)/12, 0.4, 1)Full brightness at Vmag ≤ 3
RGBclamp(1 - max(0, Vmag-1)/19, 0.7, 1)Full brightness at Vmag ≤ 1
Sizeclamp(1 - Vmag/12, 0.5, 1) × starCoreSizeLarger 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//datapacks//data/readstar/celestial/system.jsonor serverworld/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

FieldDescription
massMass (kg). 0 = fixed at parent position
radiusRadius (m). Apparent size = max(1.024, radius / distance × factor)
luminanceSelf-luminosity 0~15. >0 = star; children auto-resolve hostStar upward
unstableDirtySnowballComet flag. true renders dual-tail structure
hasAtmosphereHas atmosphere (controls overlay and glow rendering)
atmosphereHSVAtmosphere HSV color, packed int (H<<16 | S<<8 | V). H=hue, S=saturation, V=density (0~255)
starHSVBody's own color, same encoding. Luminous = emission, non-luminous = surface reflection
axisRotation axis. Zero vector defaults to (0,0,-1)
orbit.semiMajorAxisSemi-major axis (m). 0 = no orbit
orbit.eccentricityEccentricity. 0 = circular
orbit.inclinationOrbital inclination (radians)
orbit.argumentOfPeriapsisArgument of periapsis (radians)
orbit.longitudeOfAscendingNodeLongitude of ascending node (radians)
orbit.initialMeanAnomalyInitial 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 nearest luminance > 0 ancestor
  • Position = parent.position + orbit(parent.mass, gameTime)
  • Root fixed at (0, 0, 0)
  • gameTime drives 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
ComponentBitsRangeMeaning
H (Hue)16-230~255Hue (0=red → 255 wraps to red)
S (Saturation)8-150~255Saturation (0=gray, 255=pure color)
V (Value)0-70~255Density/brightness (atmosphereHSV=density, starHSV=brightness)

Example values:

BodyatmosphereHSVstarHSVNotes
Sun02172671No atmosphere, G-type yellow-white
Earth97387519732275Blue sky (H=0.58, S=0.6, V=1.0)
Mars13368351356697Faint 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//. 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/:

ScriptSourceOutput
generate_stars.pyBSC5 Bright Star Cataloguestars_named.json (361 IAU-named) + stars_numbered.json (8043 HR-numbered)
gaia_download.pyGaia Archive TAP APIgaia_bright_with_teff.vot (with effective temperature)
gaia_to_stars.pyGaia DR3 + BSC5 fallbackstars_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
    }
  ]
}
FieldDescription
nameIdentifier (reserved, no runtime effect)
positionUnit sphere direction [x,y,z], normalized to distance 100. Y=North Celestial Pole
VmagApparent magnitude (Gaia G-band or BSC5 V-band). Determines glow tier and brightness decay
colorARGB 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>/
  • `must matchsystem.json` name in lowercase
  • Requires exactly 8 PNGs with fixed filenames:
FilePhase
full_moon.pngFull
waning_gibbous.pngWaning Gibbous
third_quarter.pngThird Quarter
waning_crescent.pngWaning Crescent
new_moon.pngNew
waxing_crescent.pngWaxing Crescent
first_quarter.pngFirst Quarter
waxing_gibbous.pngWaxing Gibbous
Requirements
SpecValue
FormatPNG, RGBA 32-bit
Size16×16 or 32×32 recommended
EdgesTransparent (alpha=0)
ColorSelf-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
  1. PNGs under textures/environment/celestial/ auto-discovered by Minecraft's atlas system
  2. ReadstarSkyRenderer scans moons/ subdirectories and builds GPU buffers by group
  3. 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:

StencilPathPurpose
star_base.pngtextures/environment/star/Core (32×32 RGBA)
star_glow_low.pngsameLow glow
star_glow_med.pngsameMedium glow
star_glow_high.pngsameHigh 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

SymptomCheck
Purple/black star texturesIs star_base.png 32-bit RGBA?
Purple moon phaseMissing PNG or filename mismatch
Body not visibleDoes it have hostStar (ancestor luminance>0)?
Glow/halo not renderingIs halo.png under textures/environment/celestial/?
Atmosphere color wrongAre atmosphereHSV / starHSV packed correctly?
Data pack not applied/reload or restart
Resource pack not updatingF3+T
FOV compensation not workingstarFovCompensationStrength > 0?

© 2026 FrozenStream. Licensed under MIT.

FAQ

How to install ReadStar?

Install NeoForge for your Minecraft version, download ReadStar for the same version and loader, put the file into the mods folder and launch the game with the loader profile.

Which Minecraft versions does ReadStar support?

ReadStar has builds for Minecraft 1.21.1, 26.1.1–26.2.

Is ReadStar needed on the server?

Needed in the game and on the server

License and source
MIT Licensefree to use and share with credit to the author
Original — Modrinth↗Files unchanged, verified with SHA-512Is this your mod? For rights holders

The site uses cookies: the necessary ones for sign-in and the theme, plus the Yandex Metrica and LiveInternet visit counters — they show us how the site is used. We have no advertising cookies. More