On the streets of Facebook

Facebook is the only social network I use for anything serious. I wish it was more like the forums from 20-15 years ago, but I digress. Lately my Facebook got filled with more and more Suggested for you nonsense. Previously I used the hide feature on those, and that partly worked for a while, but now it just leads to more and more fringe stuff appearing. Eventually I conjured up some energy to deal with it.

What Stable diffusion should have come up with for the promt: Facebook Suggested for you on a good day.

I do not trust random browser plugins, so I decided to look into writing it myself. Firefox makes that very easy. This small snippet made Facebook acceptable again.

//Stupid flag to only run removal after some time, and not on each observed update
var mut = false;

//Get rid of "Suggested for you", probably needs customization everytime FB adds more divs ;) , worked on 19 Mar 2023!
function removeShit() {
	if(mut == true) {
		for (const span of document.querySelectorAll("span")) {
		  if (span.textContent.includes("Suggested for you")) {
		    console.log("Hide all the nonsense")
		    //The hardest part of all this is counting all that nesting, what is going on here.
		    var pr = span.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement.parentElement
		    if(pr.style.display != "none") {
		    	pr.style.display = "none";
			}
		  }
		}
	} else {
		//console.log("No crap seen yet")
	}
}

//Observe the entire doc
const targetNode = document

//Observe all the crap
const config = { attributes: true, childList: true, subtree: true };

// Execute on observe
const callback = (mutationList, observer) => {
  for (const mutation of mutationList) {
    if (mutation.type === "childList") {
      mut=true
    } else if (mutation.type === "attributes") {
      mut=true
    }
  }
};

const observer = new MutationObserver(callback);

// Start observing
observer.observe(targetNode, config);

//Interval to check for site changes and remove stuff.
const intervalID = setInterval(removeShit, 100);

As always, use at your own peril.

Facebook will still spice it all up with the ineffable timeline ordering, and still eats all the datas.

Astro log – Through the smog and light pollution

A month back I finally got a new stepper motor and got the tracking for my telescope working. One dark night I took it to a pretty dark spot close to Bergen and did some observations.

M42 – Orion Nebula

I have observed the Orion nebula under bad light pollution before, but this time I got to observe it with under better conditions. It was stunning. Very sharp.

The next day I took my first deep space photography ever from our apartment. The light pollution was really bad, and there was some smog as well. I also forgot that I could use a timed shot. So i think some blurring is due to the camera moving slightly after I started the exposure.

I think it turned out fine for a first:

M42 taken with a 5 – 10 seconds exposure. Very heavy light pollution. The left image is the raw image, the right ones are versions where i increased contrast and tried to remove the pollution. The latter is an attempt to make it look somewhat like what a visual observation of M42 looks like in my telescope (it is sharper when visually observed)

M1 – Crab Nebula

My original plan for the trip, was to observe M42 and the Andromeda Galaxy. I was also hoping to get to see the Flame Nebula since it was really dark. I sadly did not see any trace of the Flame Nebula so I started looking for some open clusters to look at in Taurus. While scanning for them I suddenly saw that the Crab Nebula was close, and I found it immediately. It is the first supernova remnant I have observed, and I think I saw some small amount of detail. Hoping to get a picture of it one of these days.

M31 and M110 – Andromeda Galaxy and a friend

Andromeda is a not that interesting to visually observe since it is so hard to see anything beyond the core. I think I saw some more since it was really dark, but it was very faint. These two are prime targets for a photo some day, since that should bring out some more detail.

A very welcome type-safe builders surprise

When Kotlin 1.0 came out, I tried type-safe builders a bit, with mixed feelings. They are a great way to create simple declarative DSLs for building hierarchies, but using them used to be quite a pain.

The main problem was that as the nesting got deeper, the DSL API would include methods from all the scopes. Only some of those methods were usable in the current scope. This was infuriating to use, since if you used a method in the wrong scope, it would often cause wrong behavior instead of an error. Here is an example using a simple XML DSL:

DSL gone bad example
A tag inside an attribute, WAT…

It makes no sense to have a tag inside an attribute, but this would not fail, but instead cause the below XML document to be created. It is valid XML, but probably not what was intended given the above nesting.

Resulting weird XML

Yesterday I needed to do create some XML, and I remembered type-safe builders to be great for this. I quickly whipped together the simple API above for that, but again was confronted with the scoping problem. Initially I figured out a very hacky way to provide nice errors at runtime, but then I thankfully read up on the Kotlin documentation, and in Kotlin 1.1, the @DslMarker annotation was added to ensure that only methods in the current builder scope is visible.

This moves type-safe builders from good to awesome in my book.

DSL now fails to compile
With @DslMarker annotation, the tag is not visible in the attribute scope, and this nonsense fails to compile.

I am late to the party, but this feels like a change that is easy to miss, and that does not get enough attention. Superb of the Kotlin team to address things like these!

♥ Princesses versus giraffes ♥

TLDR; I’m writing a coop multiplayer game with my daughter, this is the current result! Works in Firefox and Chrome. Use arrows to move and space to fire. Share a URL to play with a friend.

Some years ago, my daughter figured out I made some computer games, and she even played one of them quite a bit. After a while she wanted something new, and we figured we’ll make a game together. She would draw concepts and come up with ideas, and I would try to make them happen in game.

The initial concepts she drew were these:

We then together made them into vector with some modifications.

Princess and "giraffe"
A princess and a giraffe… I guess

Tips on kid friendly vector drawing programs would be very much appreciated, throw me an email or post a comment. We used Sketch, but Sketch is a bit overwhelming and distracting with all its features. I want a program which only have bezier patches and transformations on them, as well as fill, stroke and possibly opacity settings.

Going from concepts to a prototype

I had been wanting to try compile to JS with Kotlin for a while, so I started a project in IntelliJ and quickly threw something together using a plain HTLM5 Canvas.

We drew some more concepts, and after some evenings implementing we had an infinite randomly generated castle, an arrow firing princess, a hyperactive bow carrying giraffe, and a bunch of collision detection bugs (yay for rolling your own).

Wriggling out of hard requirements

After a lot of fun triggering bugs, my daughter came up with some new requirements.

I want to play with my friends, and we should all be princesses!

These are sort of hard to implement, disregarding networking, it would mean a total rewrite of how the world generation and camera worked. It would also need a solution for how to avoid someone getting stuck due to the camera movement of others and so on.

Those giraffes are in for a surprise.

After some bargaining we made some new concepts, and we agreed to add a player controlled cloud, and a bunch of new giraffes.

Adding networking

For me this meant that I would need to add some kind of networking to the game. For browser games, the choices are:

  1. Communicate with a server using WebSocket and have that relay state, or run the game on the server.
  2. Negotiate a WebRTC datachannel, and send communication directly between the browsers.
  3. Have players install a browser extension like netcode.io,and use it instead of WebSocket.

Since the game is cooperative, there is little reason to run the game on a server. Actually I really, really do not want to run the game on server, for a bunch of reasons, mostly for abuse and scaling troubles.

Using a server as a relay of state or input is also a bit funky, since it will introduce a lot of unnecessary latency. Since I am also willing to sacrifice some poor kids behind a symmetric NAT, I decided for option 2 and I have not regretted that.

I was cautious about doing this initially, since I had read this Gaffer on Games post which deemed WebRTC too complex, though that was in the context of server based architectures.

Having some more experience with WebRTC now, I agree a bit about the complexity, though I think it has gotten way better, especially with a more stable standard and more complete alternative implementations like rawrtc. I also ♥ how WebRTC abstracts away most of the P2P complications behind a very nice API.

Autorative peer or GGPO?

To share state in the game, I needed to come up with an architecture for networking. Initially I evaluated using something like GGPO, but in the end I chose to go with using the princess peer as an autorative peer, and sync the state to the cloud playing peer continuously, while the cloud peer only sends input. I chose this mostly for simplicity and time constraints. Since the game is cooperative, a lack of fairness is also not really a problem.

For the amount of work i put in, I am very pleased with how the networking worked out. Right now it is not tuned at all, just JSON over the datachannel, but even without tuning and no extra speculative integration, it has worked fairly well.

Where to go from here.

While the game is in a state of continuous updates, I think it is mostly just going to be small changes from now on. Maybe some sound effects and new graphics when we feel like it.

Rendering is currently also quite slow, and takes a lot of the frame budget. I would like to migrate to a framework with a WebGL based renderer. But sadly that seems like quite a bit of work, mostly due to using SVGs for graphics.

For future projects game projects, I will for sure start with a WebGL based framework, or possibly Unity tiny, and raster based images.

That is all for now, go and see how far you can get in our game!

One way to stop a 2D spaceship as fast as possible.

For a quite a while, I have wanted to try and create simple touch based interface for a 2D spaceship game. I want to allow the player to simply drag anywhere on the screen, and the spaceship moves to that position and direction in an efficient manner. Ideally the most efficient manner.

Spaceships in 2D games usually have one main engine that allows forward thrust, and some that allow rotation around the ships center of mass.

Moving from point A to B efficiently (in minimal time) is not trivial with such constraints, as changes to direction and thrust may have huge consequences for later possible movements due to inertia.

So instead of looking at the full A to B problem immediately, I wanted to look at something simpler first, namely to go from having velocity \(v_0\) and pointing in direction \(\theta_0\) to have 0 velocity as fast as possible.

The idea I use originally came from talking to a colleague, but something very similar sounding is mentioned in planning algorithms, though examples always seems to involve driftless systems. Anyway, my current approach involves these known quantities and assumptions:

  • \(a\) – Acceleration – The ship can only accelerate by a constant amount, and acceleration turns on and off instantly.
  • \(s\) – Turn speed – rotating the ship requires no acceleration, and the ship has constant rotation rate.
  • \(\theta_0\) – Initial orientation.
  • \(v_0\) – Initial velocity

These quantities allow me to find a legal, but very suboptimal way to stop. It simply involves to turn the ship to face its velocity vector, and then accelerate until it stops. Both the time needed to turn the ship \(t_a\) and the time \(t_m\) needed to turn and reverse the velocity are easy to calculate.

Turn until facing velocity and initiate burn at time \(t_a\). At \(t_m\) the ship has velocity 0.

It is also easy to see that this is suboptimal, it would clearly be faster, to start burning some time before the turn is fully completed, but the question is when to start the burn.

To allow for this freedom in my model, I therefore introduce a third time variable \(t_s\). \(t_s\) is the time to start turning and accelerating at the same time. \(t_a\) now becomes the time when I stop turning and only accelerate.

Turn and initiate burn after \(t_s\) time, at \(t_a\) time only accelerate. At \(t_m\) the ship has velocity 0.

Given these intervals, two integrals describe how the velocity will change when \(t_s\), \(t_a\) and \(t_m\) vary.

$$ v_x = \int_{t_s}^{t_a} a\cos(\theta_0+st)dt + \int_{t_a}^{t_m} a\cos(\theta_0+st_a)dt $$

$$ v_y = \int_{t_s}^{t_a} a\sin(\theta_0+st)dt + \int_{t_a}^{t_m} a\sin(\theta_0+st_a)dt $$

This gives two constraints, that must hold for all solutions of this kind.

$$ 0 = v_{0x} + \int_{t_s}^{t_a} a\cos(\theta_0+st)dt + \int_{t_a}^{t_m} a\cos(\theta_0+st_a)dt $$

$$ 0 = v_{0y} + \int_{t_s}^{t_a} a\sin(\theta_0+st)dt + \int_{t_a}^{t_m} a\sin(\theta_0+st_a)dt $$

The most efficient solution to this problem, is the \(t_s\), \(t_a\) and \(t_m\) triplet with the lowest value for \(t_m\).

This information allows me to formulate this as a optimization problem.

Since I want to minimise \(t_m\), the objective function simply becomes \({t_m}^2\).

This is subject to the two equality constraints given.

Since the objective and constraints are non-linear, I plug i into Optizelle which is a framework for solving non-linear optimization problems.

The implementation can be found on github, it uses autograd, to calculate derivatives and hessians. This is an incredible time saver since calculating 9 combinations of partial derivatives would have been a major pain, not to mention having to recalculate them whenever I did something wrong.

Running the program with inputs \(a=2.0\), \(\theta_0=0\), \(v_0=[2,0]\) and \(s=\frac{\pi}{2}\) returns:

running-optizelle

The optimal point vector contains the values for \(t_s\),\(t_a\) and \(t_m\). This means that for a ship with the given input, it should start turning immediately, then start the burn after approximately 1.43 seconds, stop turning and only accelerate at 2.43 and finally be at rest after 2.57 seconds, approximately 0.43 seconds faster then the naive version.

To test the result, I implemented a quick and dirty javascript program that simulates these choices and renders to a canvas:

Sometimes the ships end up drifting a bit after the simulation has finished. This is due to the discrete nature of the simulation not perfectly emulating the continuous solution (I do not integrate rotation analytically in the simulation). This could also have been a problem if I applied this style of planning to a game that did the same, from the simulation above it looks negligible though, which is great!

I am very happy with this result, it seems like it could work for the larger problem as well. The next step I’ll try, is to tackle some specific cases of moving from point A to B efficiently. For those cases there will be many more time variables involved, and possibly many constellations of safe initial starting points as well as possible freedoms to introduce in the model. It will be interesting to see how that works out.

Learning what WebRTC SDP a=setup values mean

My very hacky webRTC datachannel implementation stopped working a while back, and I could not figure out why.

The way it behaved was hard to understand. Signaling worked as expected, and I received a STUN on the correct port and responded to that. Both Firefox and Chrome reported the response as fine, and kept sending new STUN heartbeats at the normal rate, but no DTLS handshake was initiated.

Initially i thought something was really broken with my STUN/DTLS multiplexing, but I soon figured out that it behaved as expected.

This meant I was probably sending some wrong parameter during signaling, but what?

This is the SDP of my answer to the given offer from the browser.

v=0
o=- 1234567 2 IN IP4 192.168.1.158
s=-
t=0 0
a=group:BUNDLE data
a=msid-semantic: WMS
m=application 51410 DTLS/SCTP 5000
c=IN IP4 192.168.1.158
a=candidate:1 1 udp 2113937151 192.168.1.158 51410 typ host
a=ice-ufrag:4a64ca2b
a=ice-pwd:a26b6a15a8b4d35db21692d37906840a
a=ice-options:trickle
a=fingerprint:sha-256 C9:E2:48:09:47:C8:CC:B3:51:A8:A1:C5:AA:63:51:26:50:1D:FF:76:AE:EF:CB:31:0C:E7:41:21:5A:11:FA:D5
a=setup:actpass
a=mid:data
a=sctpmap:5000 webrtc-datachannel 1024

SDPs are confusing to me, and figuring out what stuff really means in WebRTC context is a lesson in reading RFCs with a microscope, while always wondering if this is the correct RFC for this concrete problem.

Suddenly it dawned on me that it seemed like both sides were waiting for the other side to initiate the DTLS handshake.

It turned out this was the problem:

a=setup:actpass

Since i responded with actpass in my answer SDP, the browser could not know if it wanted to initiate DTLS or not, and I guess it defaults to passive now. actpass is an illegal response value according to this RFC, and defaulting to passive is probably more correct then active. Setting a=setup:passive fixed the issue, since that tells the browser to be the initiating party.

Good times.

Starting Marathon Infinity in vidmaster mode on linux

A few days ago I installed Marathon Infinity for some multiplayer games. I wanted to practice a bit first, but sadly it is not possible to start a multiplayer game alone, so the only way to get some fast action is to play singleplayer in vidmaster mode.

This resulted in another problem. I could not figure out the button combination to trigger vidmaster mode on linux. After some minutes searching I was quite frustrated, but thankfully the Aleph One source is available, and the source revealed:


static bool has_cheat_modifiers(void)
{
	SDL_Keymod m = SDL_GetModState();
#if (defined(__APPLE__) && defined(__MACH__))
	return ((m & KMOD_SHIFT) && (m & KMOD_CTRL)) || ((m & KMOD_ALT) && (m & KMOD_GUI));
#else
	return (m & KMOD_SHIFT) && (m & KMOD_CTRL) && !(m & KMOD_ALT) && !(m & KMOD_GUI);
#endif
}

Based on this, vidmaster mode on linux is activated by holding SHIFT and CTRL while clicking BEGIN NEW GAME, and sure enough:

Pledging hard here

Sci-fi book review

Last year i got a kindle for Christmas. This is a review of all the science-fiction books it contains at this moment, with the exception of Robert A. Heinlein’s Starship Troopers and Joe Haldeman’s Forever War (This post is already way too long and these are pretty well known books). For each book/series I’ll try and give a very short description followed by my thoughts.

The TLDR; these books provide a balanced diet ;-).

If you only have time to read one book, read Ann Leckie’s Ancillary Justice.

Not just the TLDR

These are the books I have read in no particular order:

  • Jason M. Hough – The dire earth cycle

    Someone (not humans) has built a space elevator in Darwin Australia. After some years a disease either kills or turns everyone into zombies except in a safe zone around the space elevator.

    The dire earth cycle is a quick read, and an entertaining one. It was way better then I expected. Sometimes it goes into these very long and meaningless action sequences; you can safely skim those.

  • Ann Leckie – Ancillary Justice

    The story follows an AI fragment from the Radch starship Justice of Toren‍. This fragment is all that is left after the starship was destroyed. While Justice of Toren plans revenge on its destructor, we get flashbacks to its previous life as a ship AI in service of the Radch.

    I do not have enough positive things to say about this book. The main character is extremely well written. The pace is good. I’m really looking forward to the third book in the series. If you are going to read it, do not read about the book first, it might spoil some parts which it is worth not to have spoiled.

  • Ann Leckie – Ancillary Sword

    Not as good as the first book, but still great.

  • Kim Stanley Robinson – Mars trilogy

    We follow the first 100 colonists of Mars as they colonize and attempt to terraform mars.

    Of all the books on this list, this series really stands out as different. Most of the time the book follows the everyday work of the 100 colonists as they work, scheme, and daydream. There is no good and evil here. While the political views of the author shines through it never feels like preaching. On the negative side the book has travel descriptions that makes the travel descriptions in Lord of the Rings feel like short strolls. Still the series is one of my favourites.

  • Kim Stanley Robinson – Icehenge

    Someone made a huge monument on pluto, why?

    Set in the same universe as the Mars Trilogy. It follows some of the same style, but the pace was a bit faster. The story has a lot of references to the Mars Trilogy, so it might be better to read that first.

  • Kim Stanley Robinson – The Memory of Whiteness

    We follow the master of ‘Holywelkins Orchestra’ on its tour from the outer to the inner solar system. On the way it becomes clear that the orchestra is immensely powerful. And also some cult controls Mercury and therefore the power distribution to the rest of the solar system.

    That probably made no sense. The book seemed to make sense (and was enjoyable) for the first half, then it stopped making sense. Too weird for me.

  • Vernor Vinge – Marooned in Realtime

    In the future humanity figures out a way to suspend time in bobbles (allowing time travel to the future). The main character is unwillingly suspended and returns to a worlds where human civilisation is gone and only a few humans (bobblers from varying degrees of civilisation) are left, including his suspender.

    Vernor Vinge does a very good job with his concepts. He introduces the rules of his universe and then follows them. This book is short and to the point. No infinite traveling on Mars; no zombies. A very enjoyable read.

  • Vernor Vinge – The Peace War

    In this book we follow the world just after the bobbles (see previous book) were invented, and are discovered to be finite stasis fields.

    I enjoyed Marooned in realtime more, but it is well worth reading.

  • Vernor Vinge – A fire upon the deep

    Our galaxy is divided in zones that allow different sorts of intelligence and technology to arise and be used. We follow humanity which has traveled to the Beyond where AI and FTL travel is possible ( Earth is located in the Slow zone where these things are not possible). The outer zone is called the Transcend, where the beings are basically gods. Trying to enter the Transcend from the Beyond, some humans fall into a trap and release a being which threatens all life in the beyond. A ship escapes the trap with information on how to counter the being, but strands on a world with wolf like creatures with group-minds.

    Very interesting concepts and quite well executed. A lot of the book is written from the perspective of packs which are group-minds of several individual wolfs. For me these chapters were initially hard to follow, since I do not think it was explicitly explained that these were group-minds.

  • Vernor Vinge – A Deepness in the Sky

    This book takes place in the Slow zone (no FTL). Two human space traveling civilisations discover a world which orbits around a star that is only active for one year every 250 years. On this world lives a species of spiders which will soon reach space. The two human civilisations clash over the right to trade with/enslave this species. The clash leaves them both crippled though, and they need to cooperate while waiting for the star to wake and get new resources from the Spiders.

    I liked this book more then A fire upon the deep. There are some parts about layered complex software growing over time (they have very old software on their spaceships, like if glibc would be used several thousand years in the future), which to an enterprise programmer almost feels way too believable.

  • Christian Cantrell – Containment

    Arik must figure out artificial photosynthesis, or his not yet born child will cause the colony he belongs to on Venus to eventually run out of of oxygen.

    I was really surprised by this book. It has some great plot twists, and was very difficult to put down. I am currently in the process of reading the sequel Equinox. These books both has very brief encounters with zombies. Thankfully very short, but they would be better without.

  • Mike Resnick – Seven Views of Olduvai Gorge

    Alien archeologists come to earth to excavate after humanity is long gone.

    Short and very enjoyable read. Just read it.

  • Jon Scalzi – Old Man’s War universe

    Humanity has reached space and has settled several planets, but it is in conflict with several alien species over territory. This conflict is handled by the CDF (Colonial Defense Forces) who is in constant need of new soldier on a very deadly battlefield. These soldiers are recruited from an overpopulated Earth where the CDF controls the only access point to space. To keep the stream of soldiers the CDF largely keeps Earth in the dark of their technology and stategies. The series explores the conflicts with the alien and the political struggles resulting from this situation from the perspective of the soldiers and political figures caught in it.

    The overall quality of this series is great. If you liked Starship Troopers you will most likely like this. I do not think any single book of the series is as good as Ancillary Justice, but I read every new book in the series.

  • Conclusion

    If you only have time to read one book, you can not go wrong with Ann Leckie’s Ancillary Justice.

    Polar bears, not zombies!

    Scape – a very ninja scripting language


    I made a small scripting language that runs in the browser. It is very ninja. To see the ninja, first open Javascript console and write:

    function recur() {recur()};recur();
    

    Hopefully it blew the stack. Then type this into the Scape REPL:

    def recur() recur(); recur();
    

    When you are convinced it will infinitely loop without blowing the stack, hit ctrl-c to stop further processing.

    Rincewinds rave, that is black magic! Also called tail call elimination. Scape code is not evaluated by snarfing functions from Javascript (JS functions do not have tail call elimination before ECMAScript 6), but instead is compiled to its own set of instructions, which are then run on a stack machine (running in the Javascript VM). During parsing Scape functions are checked for whether they can use tail call elimination. If they can, they get different instructions that reuse the existing stack frame.

    More magic

    Scape has forward mode automatic differentiation as a language feature. Automatic differentiation allows you to compute the derivative of a function, without having to define the derivative explicitly.

    Without automatic differentiation, this would be the way to compute the partial derivative of the function f(x,y) =  x^{2}y^{2} for x and y:

    def fun(x,y) * (* x x) (* y y);
    def diff_fun(x,y) [* (* 2 x) (* y y),* (* 2 y) (* x x)];
    diff_fun(4,5);
    [200, 160]
    

    With automatic differentiation in Scape, this is how it is done:

    def fun(x,y) * (* x x) (* y y);
    diff(fun(4,5));
    [200, 160]
    

    This is very useful for a number of numerical methods involving derivatives. The feature is currently experimental, it might interact with non-double types in funky ways.

    Wai?

    Mostly just for fun. I also started toying with the idea to make a safe scripting language for use in networked games. A language and runtime that would allow the player to define custom logic during gameplay without being able to ruin the experience for other players.

    A dream would be a personalized Starcraft where it is you and your custom control scripts versus the other player and his scripts.

    I hope to create a simple real time multiplayer game to show how I imagine it working. For now, playing with the Scape REPL is the only way to try the language.

    Sayōnara

    Artifact-1.0.2

    Today I released Artifact-1.0.2 after finally getting my ass around to create a close to fully automated build script for Mac OS X (a topic for another blog post). The full changelog is listed below, but I instead recommend you go get it and try it!

    mech-sec-new
    New second orb graphics

    Changelog:

    1. Added full screen and resolution management in game.
    2. Removed splash screen.
    3. Adjusted difficulties and added new names (Apprentice, Journeyman, Master).
    4. Added additional fire button, allowing better control using a touch-pad.
    5. Redesigned second orb with additional graphics and new behavior.

    More in detail:

    1. The splash screen in Artifact was unnecessary and the only issue keeping me from removing it was having in-game window and resolution management.
    2. See above.
    3. The Normal and Hard game difficulties were hardly different in version 1.0.0, while the Not sane difficulty was extremely hard. Now the Apprentice difficulty is much easier then the old Normal, Journeyman is similar to the old Normal, while Master is slightly easier then the old Not Sane difficulty. The change was mainly done to make the initial difficulty easier for new players.
    4. On a touch-pad moving the mouse and clicking might interfere with each other, so I added an alternate fire welder button for those who might prefer that.
    5. The Second orb was very hard to predict and its mechanic felt wrong. The new version is cooler , and most of the time way easier to predict. It also has a slight comeback factor, which is nice in this cutthroat game.