Sublunar Almanac

JavaScript Necromancy

Right at the beginning of the last Retrograde phase that just ended in the beginning of December Mercury had dragged me down into the "underworld" of my hard-drive where all the dead and forgotten projects lie. He made me dig up an undertaking I had left abandoned over a year ago.

Back when I did Gordon White's premium member sigil course I had hacked together some horrendous JavaScript code copied and pasted from GitHub projects and ancient websites and stitched up a web-based tool called "Hygromanteia Timer" that allowed me to find "fruit machine days": Auspicious days and hours where planetary rulers, good lunar days and zodiacal moon positions could could be matched up with a desired magical goal - all as described in the Hygromanteia. Of course I could have used the spreadsheets provided by Gordon and other members and used Lunarium to find certain lunar events, or the awesome tools over at Ghostly Harmless, but I am not only an occultist but even more a nerd, I wanted to do it better myself and as an all-in-one tool.

The initial code was chaotic but served me well and it was never meant to be used by anyone else. Soon I had reached a dead end when trying to add features to it and in a spontaneous Martian mood I heroically decided to rewrite the whole thing from scratch and make it modular and extendable. But as with so many things in life, the Saturnian forces in me won by mere tenacity and I lost interest in rewriting the code and left an unfunctional wasteland of code fragments behind.

Now suddenly Hermes Cthonios had kicked me hard and within several weeks of full-on total antisocial geek mode I collected all the rubble, sorted and rebuilt it into a modular structure, and almost on the minute of Mercury stationing back direct I leaned back from my computer screen and found myself looking at a finished project I dubbed "Sublunar Almanac".

Here it is as a publicly available tool you can use to find all kinds of magically auspicious hours for your own ritual needs:

SUBLUNAR ALMANAC

Merry Christmas! Please read on for detailed instructions on how to use it and how you can help developing it further:

User Guide

The Sublunar Almanac is a browser based tool to display and filter a roster of Planetary Hours, the most commonly used time-frames for starting magical operations. The starting and ending times, as well as the durations of Planetary Hours depend on the geographic location because they are defined by the respective local rising and setting times of the sun. The Sublunar Almanac can be used to find auspicious hours based on place and astrological data and information found in grimoires and other magical texts.

Too lazy to read the whole guide? Check these video examples:


Overview

When activated properly Sublunar Almanac displays a roster of rectangular boxes, each representing a Planetary Hour.

Roster

They are numbered from 1 to 24, the yellow ones are during the day (sunrise to sunset), the blue ones during the night (sunset to sunrise). The large number in the bottom row indicates the number of the hour, the numbers in the center of the top row show the real Gregorian Calendar date in the format dd/mm. The symbol in the top left corner indicates the ruler of the current Planetary Day (Sol, Luna, Mars, Mercury, Jupiter, Venus, Saturn) and the one in the right corner the ruler of the Planetary Hour. Below you find the starting time of this hour on the left in hh:mm format and the ending time on the right, respectively.

But before the Sublunar Almanac shows you any of this, it needs to be individually set up.

Settings

Navbar

The program needs to know a geographic location and a range of hours to display, among other options. These must be set properly. To do that, click the Settings button on the top left of the grey navigation bar:

Settings

The easiest way to set up a location is by clicking the Locate Me button and allowing the browser to pass the geolocation information to the Almanac. If your browser does not support that feature or if you want to calculate the Almanac for a location that is not the one you are currently at, you can enter it manually by typing the name of your city or place into the "Manual Geolocation" field and wait for the auto-complete list to show you all the matches. When you click one of the options in the list, the fields for UTC-Offset (time difference from Universal Time), Latitude and Longitude are automatically filled in.

Alternatively you may of course provide the coordinates and offset yourself manually. These latter fields are crucial for the Almanac to function, so if they are empty, it won't work.

The other important information needed is the time-range to be displayed. The Time and Date field can be used to adjust the desired starting point of the Almanac. It is set to right now by default. The number of days for which the Almanac will calculate the hours can be set in the Generate Days field. It is set to one week by default but can take any number of days. The Almanac takes around one second to calculate all information for one day, so if you want to generate 30 or more days, prepare to be patient.

The Almanac shows you all the hours from the beginning of the day set up in the Time and Date field. If you want to only see hours that lie in the future, tick the Hide Past checkbox. By default the Almanac uses the Tropical Zodiac for Astrological calculations. If you - like myself - prefer to use the Sidereal Zodiac, please make sure you tick the Sidereal checkbox.

The bottom part of the Settings are a list of all Plug-in Modules currently implemented in the Sublunar Almanac. Each module calculates and displays different information based on astrological phenomena and lists of auspicious times transmitted through magical texts and these modules are the power behind the Almanac.

By default, all modules related to astrological phenomena and planetary timing are activated and all modules related to magical texts are deactivated, so if you want to see for example, what the Hygromanteia or PGM have to say about certain times the moon is in certain Zodiac signs, make sure to activate these modules by ticking their checkbox.

When all settings are made, click the blue Generate button and watch the magic unfold. The Settings window will close and a blue line and circle on the top of the screen will indicate the status of the background calculations. When it is done, the roster of Planetary Hours will appear according to your settings.

Hour Information

When you click any of the Planetary Hour boxes you can see all calculated information about this hour depending on the modules previously selected in the Settings:

Navbar

If you want to save this selected hour to your Google Calendar or any other calendar that supports the iCal format, such as Apple or Microsoft Calendar, click one of the buttons on the bottom of the information window.

Filtering

The main power of Sublunar Almanac is the filtering. Notice the small menus that appeared in the top right of the navigation bar after you clicked Generate? These are filters provided by the modules to find an auspicious hour for whatever operation you want to execute.

Navbar

When you click on one of them, its filter options will be displayed:

Navbar

Here you can see two columns with all the filter criteria you can apply to the hours in the roster. The left column is a list of tags related to all the criteria listed in the right column. If you click on one of the tags, only the criteria related to this tag are listed. When you click a filter criterium in the right column it will immediately be applied to the grid of Planetary Hours in the background. The filter criteria are mutually inclusive ("OR" logic), that means that when you select more than one of them, all of the selected ones are applied ("show hours matching criterium A OR B OR C").

In contrast, the filter sections themselves are mutually exclusive ("AND" logic). So if you select criteria from filter A and criteria from filter B, the calendar will just display dates that have an overlap of the criteria of A and B ("show hours matching criterium A OR B from filter A AND criterium C from filter D"). This has been done to make it possible to find Planetary Hours that have overlapping auspiciousness criteria (for example to "find an hour of Mercury on the day of Jupiter when Mercury is exalted") - in Rune Soup terms also known as "Fruit Machine" moments.

Beware: If you choose too many criteria that do not match any of the displayed Planetary Hours, the roster will become empty. To reset all the filters at the same time and display all Planetary Hours, click the Reset button in the navigation bar.

Offline Functionality

In a limited way it is possible to use the Sublunar Almanac offline. This is practical when you know that you will not have internet or when you want to calculate a larger range of days for a place and you don't want to wait for the lengthy calculation everytime you use it. When you have generated a roster it is possible to save the data by going back into the Settings and clicking on the newly appeared download button on the bottom left. Now simply leave the Sublunar Almanac open in you browser before you go offline or save it locally.

Navbar

Next time you want to regenerate the roster, simply open Settings and instead of providing location and time information as described in the Settings section above, click the Select File button on the bottom left and select the file you downloaded in the step explained in the previous paragraph. Now you can still select whether to Hide Past hours or use the Sidereal zodiac and which modules to use, but all other fields are ignored. Hit Generate and the roster is immediately generated without any necessary calculations.

License

Sublunar Almanac is free to use and also published as open source under MIT License! If you find it useful and it helps you improve your enchantments, please be so kind and Buy Me a Coffee!.

Desktop App

Since January 2019 there is also a Desktop App that has all the features of the online-tool and in addition:

  • When requesting the App, users can provide a place name or coordinates and receive 365 days of pre-calculated planetary hours that can be instantly loaded without the need of calculations. If this option is activated the App downloads the preset files upon the first activation. A Load Preset button will appear in the menu bar that can be clicked any time to load it into the grid. (Caveats: Due to sheer amount of Planetary Hours loaded into the grid with this method, the App might become unresponsive when filtering and then de-selecting applied filters. A workaround is to simple reload the presets and start over with the filtering process.)

  • When the App is used offline, the automatic location detection and the city-search function do not work anymore and are replaced by a dropdown-menu with all the last locations you calculated planetary hours for when you were online. Optionally you can always provide the coordinates for the location you want to calculated the hours for and use the Save Place button to also save it to the list of known locations.

If you are interested in getting the App, please check this article.

Notes and Caveats

Most importantly: I take no reliability or warranty on the accuracy of any of the information provided by Sublunar Almanac.

I am not a professional JavaScript programmer, but I taught myself how to code to get stuff done. Please forgive me if my code looks shit or has bugs.

Of course astrological phenomena are in constant change, from minute to minute. The Sublunar Almanac calculates all data for the start time of each Planetary Hour, so if your magical operation strictly depends on the moon being in a certain range of degrees of the Zodiac or needs to happen only on a certain Lunar day, please double-check with other sources. Usually it is magically sufficient to start a magical operation in a certain hour.

The difference between tropical and sidereal Zodiac I use is 24° 07' 02"

For technical reasons the only dates where all phenomena can be fairly accurately calculated are between 1900 and 2100 CT, Lunar Days only between 1970 and 2100.

Lunar Day 1 starts on the exact moment of New Moon, all consecutive Lunar Days start at local moonrise-times.

For astrological aspects I use orbs according to Hellenistic sources. All planets have an orb-size of 3 degrees, the luminaries 3.2 degrees for all aspects except conjunctions where the moon has 13 degrees and the sun 15. For direction and speed calculations I interpret all speeds between -0.01 and 0.01 degrees as stations for the inner planets and between -0.001 and 0.001 degrees for the outer planets, everything below that is retrograde, everything above direct.

Contribute

If you would like to contribute to the development, there are many different ways to do so:

Research: if you have access to an interesting grimoiric text or astrological system that has useful magical timing information that could be added to Sublunar Almanac as a module, find and extract the information and send it to me! info (ät) sublunar (dot) space

Invent: if you have more ideas how to make the project better, please let me know! info (ät) sublunar (dot) space

Report: if you are an astrologist or other cyclic-time-nerd, double check the information calculated by Sublunar Almanac and report errors you stumble upon! info (ät) sublunar (dot) space

Code: if you want to code your own module to plug into Sublunar Almanac head over to the module dev section or if you want to improve the overall code, check our GitHub. If you happen to have experience in javascript phone app development and know WebAssembly JS please contact me immediately! info (ät) sublunar (dot) space

Donate: support me with a bit of Mercurial cash to keep me motivated: Buy Me a Coffee!

Module Development

Module Structure

The plug-in modules are simple JavaScript objects with a fixed architecture that neatly integrate into the Sublunar Almanac. The structure of each module is the same:

:::javascript
var module_x = {

  definitions: {
    name: "Module Name",
    actions: [
      [0],
      [{
        "id": "1",
        "action": "Action description",
        "tags": "tag1 tag2",
        "hide": "info"
      }]
    ]
  },
  property: function(m) {
    return m;
  },
  calculate: function(definitions, property) {
    return definitions.actions[property];
  }
};

As you can see it is a simple JavaScript object with variables.

var module_<x> where <x> is a unique module identifier

definitions.name is a string with the descriptive name of the module to be displayed on the page

definitions.actions is an array of arrays of objects. Each object is a descriptor of an operation or phenomenon the module can display or filter according to information it gets from Sublunar Almanac. The id should be a unique number inside the module, action is a descriptive string of the operation or phenomenon, tags a space delimited list of tags associated with the operation or phenomenon. Optionally you can define hide either with info which will keep this phenomenon or operation hidden from the information window that appears when clicking on the planetary hour but visible in the filter lists, or filter which hides it from the filter lists but displays it in the information window.

definitions.property is a function that receives a JSON ephemeris object from Sublunar Almanac for each planetary hour calculated. In the function you can modify or filter anything from this object and return it to the next function:

definitions.calculate is the main function that calculates and relates the property object received from the previous function with any data of the whole definitions object itself (mainly for actions). It should return an single dimension array of objects defined in the actions part of the module.

Optional variables:

definitions.core with boolean true/false defines it as a core module that is enabled by default in the Settings

definitions.group defines the module as being part of a group of modules

definitions.group.id unique identifier of the group. All modules belonging to the group should share the same id.

definitions.group.text string of the group name how it appears throughout the Sublunar Almanac

definitions.group.info with string true sums up all submodules informations under the headline of the group.text in the information window

Helper Functions

Here are some functions that can be called from within the module to calculate certain things. The function involving the zodiac respect the setting for tropical or sidereal in the Settings

SL.Astro.Nomy.moonPhase(object) function that takes an object argument with properties angle and day as provided by the JSON ephemeris object and returns the phase of the moon (1 - 8)

SL.Astro.Logy.zodToDeg(string) function that takes a string argument in the form "12 Aries 24" or similar and calculates a decimal degree (tropical ecliptic longitude)

SL.Astro.Logy.degToZod(float, sidereal) function that takes a float argument (tropical ecliptic longitude) and turns it into a string like "12 Aries 24" (degrees, sign, minutes)

SL.Astro.Logy.getZodiac(float) function that takes a float argument (tropical ecliptic longitude) and returns an integer indicating the zodiac sign of the degrees (1 to 12)

JSON Ephemeris Object

All loaded modules are called each time a planetary hour is calculated and the property function receives a JSON ephemeris object with the following data as a base of all the module's calculations and returned information:

:::javascript
{
"ts": 1544161663,
"lunar": {
  "day": 1,
  "angle": 176.388282,
  "phase": 0.000993
},
"planetary": {
  "day": {
    "no": 5,
    "start": 1544161663,
    "end": 1544205306
  },
  "night": {
    "start": 1544205307,
    "end": 1544248088
    },
  "hour": {
    "no": 0,
    "start": 1544161663,
    "end": 1544165299,
    "length": {
      "day": 3637,
      "night": 3565 }
    }
  },
  "ephemeris": {
    "sun": {
      "deg": 255.057706,
      "speed": 1.015755
    },
    "moon": {
      "deg": 254.313237,
      "speed": 12.599819
    },
    "mercury": {
      "deg": 237.281215,
      "speed": 0.060434
    },
    "venus": {
      "deg": 212.702776,
      "speed": 0.646929
    },
    "mars": {
      "deg": 343.510950,
      "speed": 0.651342
    },
    "jupiter": {
      "deg": 246.386171,
      "speed": 0.222509
    },
    "saturn": {
      "deg": 278.507393,
      "speed": 0.111683
    },
    "asc": {
      "deg": 258.847077
    },
    "mc": {
      "deg": 166.819098
    }
  }
}

ts is the unix epoch timestamp of the calculated hour

lunar.day is the current lunar day (0 to 29)

lunar.angle is the phase angle of the moon to the sun (0.0 to 180.0)

lunar.phase is the percentage of illumination (0.0 to 1.0)

planetary.day.no is the current day of the week from Sunday to Saturday (0 to 6)

planetary.day.start is the start of the day (sunrise) in unix epoch format

planetary.day.end is the end of the day (sunset) in unix epoch format

planetary.night.start is the start of the night (second after sunset) in unix epoch format

planetary.night.end is the end of the night (second before sunrise) in unix epoch format

planetary.hour.no is the number of the planetary hour (0 to 23)

planetary.hour.start is the start of the hour in unix epoch format

planetary.hour.end is the end of the hour in unix epoch format

planetary.hour.length.day is the number of seconds a planetary daytime hour on the current day has

planetary.hour.length.night is the number of seconds a planetary nighttime hour on the current day has

ephemeris.<planet>.deg tropical ecliptic longitude position of the planet in decimal degrees (0.0 to 360.0)

ephemeris.<planet>.speed tropical ecliptic speed of the planet in decimal degrees per day (0.0 to 13.0)

ephemeris.asc.deg tropical ecliptic longitude position of the ascendant in decimal degrees (0.0 to 360.0)

ephemeris.mc.deg tropical ecliptic longitude position of the midheaven in decimal degrees (0.0 to 360.0)

Example

How all this works together you will hopefully understand better looking at this example module that provides filters and information about the sign of the moon for the planetary hour:

:::javascript
var module_moon = {

  definitions: {
    core: true,
    name: "Moon Sign",
    group: {
      id: "astrology",
      text: "Astrology",
      info: "true"
    },
    actions: [
      [0],
      [{
        "id": "1",
        "action": "Moon in Aries",
        "tags": "moon aries",
        "hide": "info"
      }], [{
        "id": "2",
        "action": "Moon in Taurus (Exalted)",
        "tags": "moon taurus exalted"
      }], [{
        "id": "3",
        "action": "Moon in Gemini",
        "tags": "moon gemini",
        "hide": "info"
      }], [{
        "id": "4",
        "action": "Moon in Cancer (Domicile)",
        "tags": "cancer moon domicile"
      }], [{
        "id": "5",
        "action": "Moon in Leo",
        "tags": "moon leo",
        "hide": "info"
      }], [{
        "id": "6",
        "action": "Moon in Virgo",
        "tags": "moon virgo",
        "hide": "info"
      }], [{
        "id": "7",
        "action": "Moon in Libra",
        "tags": "moon libra",
        "hide": "info"
      }], [{
        "id": "8",
        "action": "Moon in Scorpio (Fall)",
        "tags": "moon scorpio fall"
      }], [{
        "id": "9",
        "action": "Moon in Sagittarius",
        "tags": "moon sagittarius",
        "hide": "info"
      }], [{
        "id": "10",
        "action": "Moon in Capricorn (Detriment)",
        "tags": "moon capricorn detriment"
      }], [{
        "id": "11",
        "action": "Moon in Aquarius",
        "tags": "moon aquarius",
        "hide": "info"
      }], [{
        "id": "12",
        "action": "Moon in Pisces",
        "tags": "moon pisces",
        "hide": "info"
      }]
    ]
  },
  property: function(m) {
    return SL.Astro.Logy.getZodiac(m.ephemeris.moon.deg);
  },
  calculate: function(definitions, property) {
    return definitions.actions[property.sign];
  }
};

Notes: you can see that this module has the id "moon" and the name "Moon Sign", it is a core module and enabled by default in the Settings, it belongs to a whole group of modules called "Astrology". The actions are sorted by Zodiac sign and only the ones that show the moon in a sign where she is in domicile/exalted/detriment/fall should be displayed in the info window, all others are hidden, yet still visible in the filter window. The property function takes the ephemeris.moon.deg degree and passes it to a public function SL.Astro.Logy.getZodiac (described below) and returns the result. The calculate function takes the result object, takes the sign property ( 1 to 12 in the Zodiac ) and returns the appropriate object array from definitions.actions back to the Almanac.

Show Comments