Skip to the content

Wakusei Planets惑星

The Sun, the planets and their moons as a clean line drawing, a shaded realistic drawing or a painted surface map, with real sizes, tilt and rotation, and the moons where they are at any date and time.

Wakusei is the Japanese word 惑星 for a planet. About the name

Contents

How-to

A star from its numbers

Draw any star from a few numbers (temperature, radius, class, how it turns) in 3D or as a line drawing, and say honestly what is measured and what is drawn for looks.

<wakusei-body star="..."> draws a star the way it draws a planet: turnable in 3D (realistic) or as an outline (line), through the page's one shared WebGL context. The package holds no catalogue of stars. You give it the numbers and it draws from them; it never copies a picture.

NumberWhat it sets
temperatureKthe colour (a black body at that temperature: red for 3,600 K, white-yellow for the Sun, blue-white for Vega), and how much the edge darkens
radiusSunthe size beside the Sun, and the rings drawn round the star in the line look (the Sun, the Earth's orbit, Jupiter's orbit). Betelgeuse is about 764 times the Sun's radius; its mass, 11 to 20 Suns, is another number
luminosityClassthe grain of the surface: Ia, Iab, Ib a few giant cells, III coarse, V fine, D (a white dwarf) smooth
rotationDayshow it turns on the page: faster stars turn faster, sped up and labelled (the Sun is shown 73,000 times faster than real). Left out, a slow turn is drawn and the picture says so
activityspots, and prominences and flares on the edge. Left out it follows the temperature and the class: none on hot stars, many on cool dwarfs and red dwarfs
oblatenessthe squashed shape, and the hotter poles with a darker equator (gravity darkening): Vega is about 0.11 and Altair 0.2
coronathe strength of the soft glow, 0 to 1
estimatedthe numbers above that are estimates, so the picture can say so

In a plain page

The numbers go in attributes, with star naming the star. The star's own look is inside the element, so there is one script and no entry point per body.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>A star from its numbers, from jsDelivr</title>
    <script type="module" src="https://cdn.jsdelivr.net/npm/@johnmorrisdotca/wakusei-planets/element/+esm"></script>
  </head>
  <body>
    <wakusei-body star="Betelgeuse" temperature-k="3600" radius-sun="764" luminosity-class="Iab" look="realistic" size="320"></wakusei-body>
  </body>
</html>

star can also hold the numbers as JSON, star='{"temperatureK":3600,"radiusSun":764}', or the name of a star you registered with registerStarLook (below). Attributes laid beside it win. The star property takes an object. A star with numbers that do not fit says what is wrong in data-error and draws nothing.

As an ES module

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>A star with Vite</title>
  </head>
  <body>
    <wakusei-body look="line"></wakusei-body>
    <script type="module" src="./main.ts"></script>
  </body>
</html>
import "@johnmorrisdotca/wakusei-planets/element";
import type { WakuseiBody } from "@johnmorrisdotca/wakusei-planets/element";
import { starLook, type StarNumbers } from "@johnmorrisdotca/wakusei-planets";

const betelgeuse: StarNumbers = { name: "Betelgeuse", temperatureK: 3600, radiusSun: 764, luminosityClass: "Iab", estimated: ["radiusSun"] };

const star = document.querySelector<WakuseiBody>("wakusei-body")!;
star.star = betelgeuse;
console.log(starLook(betelgeuse).parts.map((part) => `${part.part}: ${part.status}`).join(", "));

React

import { useEffect } from "react";

const BETELGEUSE = { name: "Betelgeuse", temperatureK: 3600, radiusSun: 764, luminosityClass: "Iab" };

export function StarView({ star = BETELGEUSE, look = "line" }: { star?: typeof BETELGEUSE; look?: "line" | "realistic" }) {
  useEffect(() => {
    // The element needs a browser, so it is imported here, never at the top of the file: a server render never reaches an effect.
    void import("@johnmorrisdotca/wakusei-planets/element");
  }, []);
  return <wakusei-body star={JSON.stringify(star)} look={look} />;
}

The element needs a type for React, once:

import type { DetailedHTMLProps, HTMLAttributes } from "react";

declare module "react" {
  namespace JSX {
    interface IntrinsicElements {
      "wakusei-body": DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement> & { star?: string; look?: string; size?: number | string };
    }
  }
}

Vue

<script setup lang="ts">
import { onMounted } from "vue";

withDefaults(defineProps<{ star?: object; look?: "line" | "realistic" }>(), {
  star: () => ({ name: "Betelgeuse", temperatureK: 3600, radiusSun: 764, luminosityClass: "Iab" }),
  look: "line",
});

// The element needs a browser: import it once the component is on the page, never while a server renders it.
onMounted(() => {
  void import("@johnmorrisdotca/wakusei-planets/element");
});
</script>

<template>
  <wakusei-body :star="JSON.stringify(star)" :look="look"></wakusei-body>
</template>

Vue must be told the tag is an element, once:

import { createApp } from "vue";
import StarView from "./StarView.vue";

const app = createApp(StarView);
app.config.compilerOptions.isCustomElement = (tag) => tag === "wakusei-body";
app.mount("#app");

Angular

import { Component, CUSTOM_ELEMENTS_SCHEMA, Input, OnInit } from "@angular/core";

@Component({
  selector: "app-star-view",
  standalone: true,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `<wakusei-body [attr.star]="json" [attr.look]="look"></wakusei-body>`,
})
export class StarViewComponent implements OnInit {
  @Input() star = { name: "Betelgeuse", temperatureK: 3600, radiusSun: 764, luminosityClass: "Iab" };
  @Input() look: "line" | "realistic" = "line";

  get json(): string {
    return JSON.stringify(this.star);
  }

  ngOnInit(): void {
    // The element needs a browser: ngOnInit also runs when a server renders, so the import waits for one.
    if (typeof window !== "undefined") void import("@johnmorrisdotca/wakusei-planets/element");
  }
}

Svelte

<script lang="ts">
  import { onMount } from "svelte";

  let { star = { name: "Betelgeuse", temperatureK: 3600, radiusSun: 764, luminosityClass: "Iab" }, look = "line" }: { star?: object; look?: "line" | "realistic" } = $props();

  // The element needs a browser: onMount never runs on the server.
  onMount(() => {
    void import("@johnmorrisdotca/wakusei-planets/element");
  });
</script>

<wakusei-body star={JSON.stringify(star)} {look}></wakusei-body>

In an iframe

The embed page takes the same names in its address: star, temperature-k, radius-sun, luminosity-class, rotation-days, activity, oblateness, corona, estimated and look.

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>A star in an iframe</title>
  </head>
  <body>
    <iframe
      src="https://johnmorrisdotca.github.io/wakusei-planets/embed.html?star=Betelgeuse&temperature-k=3600&radius-sun=764&luminosity-class=Iab&look=line"
      title="Betelgeuse, drawn from its numbers"
      width="420"
      height="420"
      loading="lazy"
      style="border: 0"
    ></iframe>
  </body>
</html>

The demo's star page writes this code, and the other ways above, for the star you set up.

Without a page

starLook works the numbers out, starLineSvg draws the outline, and neither needs a browser:

import { starLineSvg, starLook } from "@johnmorrisdotca/wakusei-planets";

const betelgeuse = starLook({ name: "Betelgeuse", temperatureK: 3600, radiusSun: 764, luminosityClass: "Iab", estimated: ["radiusSun"] });
const drawn = betelgeuse.parts.filter((part) => part.status === "illustrative").map((part) => part.part);

console.log(betelgeuse.colour, starLineSvg(betelgeuse).startsWith("<svg"), drawn.join(","));
#ffc392 true surface,spots,rotation,prominences,glow

A page that draws a few stars by name registers them once and writes only the name:

import { registerStarLook, starLookOf } from "@johnmorrisdotca/wakusei-planets/look";

registerStarLook("Proxima Centauri", { temperatureK: 3042, radiusSun: 0.154, luminosityClass: "V", rotationDays: 83.5 });
console.log(starLookOf("proxima centauri")?.parts.length);
9

What is measured and what is drawn

Every star carries parts, and the element's starLook property and its label say it too: drawn from its numbers, not a photo. Each part is one of four:

  • measured: a number you gave;
  • estimated: a number you gave and marked in estimated;
  • derived: worked out from those numbers by a stated rule (the colour from the temperature, the darkening toward the edge, the hot poles of a flattened star);
  • illustrative: invented to look right: the pattern of the cells, where the spots are, the prominences and flares, the glow. They follow the numbers (how many, how coarse) but they are not observations.

Quality and speed

The star goes through the same shared renderer as every body: one WebGL context, at most 30 frames a second, nothing drawn at rest or out of sight, still turning under reduced motion (a quarter as fast), a lost context recovered. In full quality the granulation boils and an active star has prominences and flares on its edge, but only while frames are being drawn. quality="low" (or starLook(numbers, { quality: "low" })) gives a still surface and a soft glow for weak devices; the element chooses it by itself on a device with 2 GB of memory or fewer, or two cores or fewer, unless quality="full" says otherwise.

The Sun

The Sun keeps its own look, painted map and line drawing (body="sun"), unchanged. STAR_SUN holds its numbers if you want it drawn as a star beside the others.

View sourceEdit on GitHubMade from the source of @johnmorrisdotca/wakusei-planets 0.25.0.