npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@g-js-api/g.js

v5.7.10

Published

G.js - Create Geometry Dash levels (with a SPWN-like syntax) in JavaScript

Readme

G.js logo

G.js - Create Geometry Dash levels (with a SPWN-like syntax) in JavaScript

Go to GitHub repo stars - G.js forks - G.js

Go to project documentation

Platforms available on

The project now supports these platforms:

  • Windows
  • MacOS
  • Linux
  • Android

Support

For support on how to use G.js, join the Discord server: https://discord.gg/GwVd7K2cQY

Installation

Use G.js in your project by running this in your project (with Node.js installed):

npm install @g-js-api/g.js

Example

Here is a functional example of G.js:

import '@g-js-api/g.js'

// configures G.js, so it knows how to export to Geometry Dash
// this NEEDS to be ran before anything else GD-related!
await $.exportConfig({
  type: 'savefile', // you can change this to 'live_editor' if you want to use it using the WSLiveEditor mod, or 'levelstring' if you only want to export the levelstring (make sure to store the result in a variable!)
  options: { info: true } // displays level info when the program finishes running, check https://g-js-api.github.io/G.js/module-index.html#~save_config for a list of options
});

// for a simple example, let's create some moving text
// first, store a group in a variable (this is akin to doing 'Next Free' in GD)
let my_text = unknown_g();

// now, add some text at X 45 Y 45 with the group ID we defined 
// GD uses small-step units internally (3x big step), meaning the block is 15 steps from the origin in-game
'Hello, World!'
	.to_obj()
	.with(obj_props.X, 45)
	.with(obj_props.Y, 45)
	.with(obj_props.GROUPS, my_text)
	.add();

// let's make a loop that moves this text left and right forever
// a 'trigger function' is a system of Geometry Dash triggers
let moveloop = trigger_function(() => {
  let my_context = $.trigger_fn_context(); // stores the ORIGINAL group of the trigger function (it will change later! this is called "context")
  my_text.move(30, 0, 0.5); // moves the text forwards 30 big step units with a 0.5 move time
  my_text.move(-30, 0, 0.5); // afterwards, it moves the text BACK 30 big step units to its original place

  // flashes the background white WITHOUT changing the context of triggers
  ignore_context_change(() => log.runtime.flash());

  // after the two moves, the "context" changes (meaning spawn delays were applied in-between, therefore newer triggers have different group IDs than in the past)
  // so to loop it, you can just call the group of the original context after all operations are finished
  my_context.call(); 
})

// now finally, we can spawn this loop!
moveloop.call();

Features that make this different from SPWN:

  • Inherits speed and ecosystem from JS (JS might sometimes be considered slower than others, but it is much faster than SPWN + ecosystem is much bigger than SPWN)
  • Has early 2.2 features (9999 group limit, item triggers, ID remapping, 2.2 obj props, gradient trigger, level options, keyframes, random + adv random, sequence trigger, song trigger, particle system, etc.)

Notes

This is not finished yet - do not expect it to work exactly as SPWN yet! Docs can be found at https://g-js-api.github.io/G.js, but they are incomplete. Usage section below might help explaining more of the complex or unclear features.

Usage

G.js has mostly the same usage as SPWN, with exceptions:

  • File must start with $.exportConfig(...) for exporting to a level (check example above or docs)
  • Since JS cannot do something like !{} for trigger functions, use trigger_function(() => { /* ... */ }) for trigger functions
  • GD objects are just done with normal JS objects, obj props are mostly the same
  • Use while loops with the less_than(a, b), equal_to(a, b) or greater_than(a, b) functions followed by a normal function, not trigger function
  • For comparing a counter with a plain number, use counter.if_is(SMALLER_THAN | EQUAL_TO | LARGER_THAN, trigger_function)
  • Counter operations are counter.add(num | counter), counter.subtract(num | counter), counter.multiply(num | counter) and counter.divide(num | counter)
  • extract x statements in SPWN are extract(x) in G.js
  • gamescene library from SPWN can be used in G.js by doing gamescene() instead of import gamescene
  • Ranges in G.js are done with range(a, b)
  • Getting unknown IDs for groups/blocks/colors can be done using unknown_g()/unknown_b()/unknown_c()
  • Group, block, and color IDs are represented using group(id), block(id) and color(id)
  • Instead of calling groups by using 1g! in SPWN, you can do group(1).call()
  • counter.to_const(a..b) in SPWN is done inside of G.js by using counter.to_const(range(a, b), (number) => { /* ... */ })
  • Counter comparison can be done using compare(counter_1, EQUAL_TO/GREATER/GREATER_OR_EQ/GREATER_OR_EQ/LESS_OR_EQ/NOT_EQ, counter_2, true_id, false_id)
  • ID remapping can be done through my_trig_func.remap([group(3), group(2)], [group(7), group(4)]);
  • Item edit trigger: $.add(item_edit(item_1, item_2, target_item, type_1 (NONE/ITEM/TIMER/POINTS/TIME/ATTEMPT), type_2 (NONE/ITEM/TIMER/POINTS/TIME/ATTEMPT), target_type (NONE/ITEM/TIMER/POINTS/TIME/ATTEMPT), assign_op (EQ/ADD/SUB/MUL/DIV), op1 (EQ/ADD/SUB/MUL/DIV), op2 (EQ/ADD/SUB/MUL/DIV), mod, absn1 (ABS/NEG), absn2 (NONE/ABS/NEG), rfc1 (NONE/RND/FLR/CEI))
  • Remappables are a sort of trigger function that can take in IDs as inputs through ID remapping:
let bl = group(10);
let fn = remappable(my_gr => {
	group(my_gr).move(10, 0);
});
wait(0.5)
fn(bl);
  • Particle systems have its own property system, check particles.js for info. Example:
$.add(particle_system({
	MAX_PARTICLES: 30,
	DURATION: -1,
	LIFETIME: 1,
	LIFETIME_VAR: 0.3,
	EMISSION: -1,
	ANGLE: 90,
	ANGLE_VAR: 90,
	SPEED: 29,
	POSVAR_X: 11,
	START_SIZE: 2,
	START_SIZE_VAR: 1,
	END_SIZE: 1,
	END_SIZE_VAR: 1,
	START_R: 1,
	START_G: 1,
	START_B: 1,
	START_A: 1,
	END_R: 1,
	END_G: 1,
	END_B: 1,
	END_A: 1,
	ADDITIVE: true
}).with(X, 200).with(Y, 100));

To-do list for 2.2 (88% done):

  • [x] item comp
  • [x] item edit
  • [x] item pers
  • [x] camera static
  • [x] camera offset
  • [x] teleport
  • [x] timer
  • [x] song
  • [x] gradient trigger
  • [x] keyframe system
  • [x] reverse trigger
  • [x] level options
  • [x] gravity
  • [x] sequence
  • [x] all camera triggers
  • [x] particle systems
  • [ ] sfx trigger
  • [ ] shader triggers