One important thing to note is that is not about eliminating all state (that would be absurd after-all a game is all about the state of the main character), but crucially to never take the `t - 1` state of the thing being animated. In games for example, a function animating a blade of grass may take into account many parts of the game state, they often take the character position as a multiplier to amplify the movement if the character is close, imitating collision with the character without actually having to calculate collisions.
For instance, here is a DVD logo bounce animation in Shadertoy:
I recently played Minnit [1], a very small and cute game with a very unique proposal: that you only have 1 minute of gameplay before being warped to a starting point.
But time is not checked asynchronously but as a function of the frame rate, so my playthrough was basically sped up and lasted for 45 seconds each time. Turned out I didn't realize until almost the end, when a _very_ difficult movement seemed almost impossible to make in time (I did end up achieving 100% doable things in the game, but it cost me a good amount of attempts!)
Gamedevs, please, do not count frames and assume they'll be whatever amount per second you believe they'll be. Because they won't.
Anyway, now I wonder if this could be done in this style. Bullet position = its origin, vector and then you can determine its location at any point in time, instead of updating its position at every tick.
Doing that one will likely cause issues across a network or if framerate (or game tick rate) is unstable / unpredictable.
Yes, the technical term for this is Euler integration: https://en.wikipedia.org/wiki/Euler_method
It's good enough for a lot of parts of a game simulation, especially if you have a fixed framerate. But it can get wonky if you have a lot of stuff moving around this way and interacting with each other. Full fledged physics engines in games tend to use more sophisticated algorithms to do the integration to avoid that.
> Bullet position = its origin, vector and then you can determine its location at any point in time, instead of updating its position at every tick.
Unfortunately, no. The linked blog post is a really cool way to think about simple animations but you will very often hit a wall (metaphorically and literally) where this technique no longer works.
The key problem is that the state of an object at time T depends on all possible interactions the object may have had before T. In the linked article, the only interactions are bouncing off walls, which are regular enough in time that you can model them analytically.
(But even in this trivial example, it still doesn't really work. Notice that if you resize your window, the DVD box jumps all over the place. That's because the analytic solution can't understand the notion of a window whose size changes. All it can do is calculate where the box would be now if the window had always been its current size. I digress.)
You can analytically calculate the position of a bullet at any time T given its origin and velocity, but only if the bullet doesn't interact with anything else. If you have, say, a human controller play that is running through the path of the bullet and gets hit, your simulation needs to understand that the bullet won't keep moving after that.
For a use case as simple as this, you may be able to model it statefully by just deleting the bullet entirely once it hits something. But, in general, there is always some level of state that you'll need in order to simulate anything beyond trivial complexity.
It certainly can! Not an expert, but what you are describing can probably be done as a bullet shader.
> Doing that one will likely cause issues across a network or if framerate (or game tick rate) is unstable / unpredictable.
I kind of want to know if a seamless experience is possible without a server... but for now I assume there is one. At that point client frame rate / tick rate does not really matter. Each client sends current character position and origin + vector of all the bullets to the server. Note that current location of the bullets is not important to update the world state - only the origination point and direction. Based on that it can independently do all the hit calculations. Of course this makes cheating possible and any kind of latency extremely annoying, but margins of this post and my available coffee break time are too thin to offer my amateurish take on those problems.
This, and shaders in general, are great examples of better ways to do things.
Often: Dynamic calculation > state
EDIT to add: Also https://www.youtube.com/@MartinDonald has some good stuff as well. And here's a subset of Handmade Hero you might find interesting: https://www.youtube.com/playlist?list=PLEMXAbCVnmY6Mnkt-EZC_...
As for game logic-it does make sense. Tying logic and rendering to a system timer / frame counter leads to all kinds of issues.
Think of it like this: Imagine you have an expensive tween animation for particles, and every frame you have to use your CPU to calculate where the particle should have moved as part of it's animation, and send those updated coordinates to the GPU. Imagine you have millions of such objects, so it's quite the burden for the CPU, both in terms of calculating the tween animation, but also in terms of constantly updating every particle's x,y values over and over.
Instead, you could seed each particle on the GPU with x1,y1,x2,y2 values, and then just provide a global "time" value for them all to share. When you update time = time + 1, all particles on the GPU will recalculate their position without needing to be helped by the CPU. The trick is that they don't save this new position though, instead, they do the job all over again from scratch at time = time + 2, which is a lot cheaper since we didn't need to "save" our previous result, which is hard work on the GPU.
We will still have state, just it will live only in data stores (like Postgres) or infrastructure components (like Kafka). Most dev work will be writing declarative code for manipulating and providing access to such systems.
I even think this will reach front end development, where the UI is an access layer to a local datastore.
I think this type of pattern (not necessarily redux specifically) is definitely the future of Frontend state if it’s not already the current paradigm
React: f(State) => UI
Redux: (State, Action) => newState
Bare in mind this is only for web dev.
Many other things outside of web dev cannot abstract state so easily. For example: The person who programs the database itself must deal with state.
Not all problems reduce this far, just because one does it does not mean they all do.
There are really only two ways of describing the world around us, by state or by function, and not all problems can be either. In tis casse the state (time) is applied to a function. This is good for some things and not neccesarily so good for others.
If you think that there is some future of computing with universal stateless code you are delusional.
Of course, none of that is relevant to how useful it is to be able to reduce "state" down to the singular (and mostly "invisible") property of "time". It's just that while you are technically correct that the article did not 'remove state', it's only as technically correct as saying a banana is a "berry". In the vast majority of use cases, including teaching people the value of something, the specification is a difference without a distinction.
The goal is to produce optimal software not to serve particular paradigm.
Hilariously, my girlfriend always yells at me "I'm not a block of code! I'm not 0 or 1! I'm not just on or off! Humans have feelings and emotions in the gray!" when I try to boil down "if this, then that" logic to real world problems.
You can also see a connection to a version control system like Git. Instead of keeping snapshots of all the contents of the repository after each commit, one can keep only the initial repository state and changes in each commit. Then to get to N-th state you say "Apply first N commits to the initial state".
In the bouncing DVD logo example the "function to compute next state" or "commit contents" is just easy and regular, to the point of being expressible via simple math functions.
This is also known as Event Sourcing.
Not to invalidate your point, but this is a common misconception with git. Yes, many git commands will treat a commit as a diff compared to its parent(s), but the actual commit object is storing the full state of the tree. This still works out to be fairly efficient because if a file is identical between two commits, it is only stored once, and both commits point at the same stored file.
Each derived state is a pain and has to be maintained but might be essential.
Hmmm....that depends on what you define as the "correct" position. With the stateless version, any resizing of the window will cause the logo to instantaneously jump to where it would have been, and start moving in the direction it would have been, if the window had been the new size all along.
Is that the correct behaviour though? Or would you want the logo to keep moving from it's current position, in it's current direction, if that were possible?
I treat it as a cost that needs to be managed and that we want to make sure to exploit its advantages for those costs, but it is still often still something that is better in the system.
In this case, a hybrid approach can be useful. Writing the animation in terms of the initial state and the time is quite useful and powerful on its own terms, something worth doing. On the other hand, dealing with an event stream of changing resolutions, while possible, also nukes all the cleanliness out of the solution. So what do we do? When the window resizes, reseat the original parameters. The animation as written implicitly starts at 0,0 and has an implicit direction in it. Bringing those out as parameters isn't a bad idea anyhow, because that may not be the ideal. Then, when the window resizes, simply look at the current parameters and start a new animation with those as the initial state, handling problems with window becoming too small or something as some simple checks on the initial parameter.
This yields a nice mix of both the advantages of statelessness and the advantages of state. It's a continuum rather than a binary, and the optimum is not always the extrema. Sometimes it is, but not always.
def ball_position(time):
if time < 0:
return (0, 0)
stop_time = 2 * Y_VELOCITY / GRAVITY
if time > stop_time:
return (X_VELOCITY * stop_time, 0)
x = X_VELOCITY * time
y = (Y_VELOCITY - GRAVITY * time / 2) * time
return (x, y)To avoid that, I'd use percentage positioning and have all widths and heights from 0-100. This would mean it would move faster horizontally than vertically in a wide window, but that's probably OK.
You may use a bounding box which has a fixed size to avoid this.
You cannot have a dynamically sized bounding box and still expect the colour to be the same, since the number of bounces since a fixed start time depends on the size of said box.
To get all the way there you need a couple tweaks:
function update(time: number, random: number): { left: number, top: number } {
First, the random number for each render gets pulled out into a function argument, making the contents of the function deterministicSecond, the function now returns left/top instead of setting them onto an element directly
(we may also now want to rename it from "update" to something like "getPosition" to reflect its new role)
With these two changes, this function is now extremely easy to test, apply to other use-cases, etc. It's nothing but input/output. This is something we in general should try to do with as much business logic as possible.
(One caveat that's specific to animations: returning these values as an object instead of setting them directly may, in JavaScript, result in doing allocations on every frame which may harm performance. But this is only really a concern in real-time domains like graphics, where the function may be called 60+ times per second. It could also be avoided by splitting this into two separate functions, one for `top` and one for `left`, so the numbers don't have to be bundled up into an object to be returned.)
EDIT: I just noticed it grabs the window and logo dimensions directly too. Those would also need to be pulled up into function arguments for this to be a pure function
This is probably a bad idea for performance too, as you'll potentially be doing lots of calculations twice.
But ofc in a real scenario where this matters you'd only want to optimize based on what a profiler says
It's just not possible to calculate most stateful things. It's interesting to me since I am currently working on a state manager where we have many "apps" on in the same app. The user needs to be able to switch between apps and only some state is going to be shared.
Thus, I need to store the state somewhere and it's not going to be in the url or in some calculated way since it's based of user selections.
It's just changing who is responsible for managing the state. The gradually increasing Time constant is still state, and it's still got code storing it, and gradually increasing it. This is _exactly_ the same thing as storing and changing X, Y coordinates, you've just outsourced the work and created code that's dependent upon the side-effect of an external system.
If we want to get really pedantic it's also adding a dependency, but if you can't trust the system clock you've got bigger problems than having an additional dependency. ;)
It's definitely not exactly the same, since the Simulation is framerate dependent while the Animation is not.
For things like particle effects in games or projectiles, the line starts to get blurrier. At some point, collision detection rears its head, and you end up storing state after all.
Right tools for the job as always!
It's ironic because the author is already storing this in the css.
But you raise a good point that the author is storing the previous state in CSS. I guess the author's counter argument would be that they can still avoid storing the current direction.
I'll Show Myself Out.
Now try to do the same little trick for a cellular automaton.
Good luck.
Of course I am very curious about the sort of misunderstanding that you are trying to prevent, if you have any examples.
It's mostly a tradeoff and a dubious one. I would rather have some "dumb stateful architecture" that can be debugged trivially.
An example of how this pattern fails to scale is skeletal animation in 3D games. You will usually have some function that takes some ellapsed time as input (sounds familiar?) and multiplies together a bunch of matrixes to transform the joints of your model. You absolutely want your code to be a "simulation" rather than an "animation" when you need to make some adjustment.
It's not that you "don't need state", it's that your state calculation is entirely dependent on time T and initial conditions I, and it's calculable in ~roughly~ the same amount of time as an actual state update would take.
The algorithms can even appear more intuitive when you remove the linear text trappings of traditional programming languages, because you can see at a glance which nodes are the changing input values and which are the dependent operations. (To a point — once the graphs get too big to comfortably fit on the screen, the clarity advantage starts to disappear. At that point you'd better hope the tool offers some kind of grouping/nesting functionality to manage the growing complexity.)
Node UIs can also offer immediate visual feedback on intermediate states, which makes it easier to tweak the algorithm compared to traditional debugging.
> If you resize the window to be smaller you can trap the DVD logo on an edge. Resizing the window in the Animation solution means we recalculate to the correct position.
If you resize the window in a simulation state it's trivial to maintain the same general position of the logo. For instance, 80% across the screen or whatever. With the authors code, as the window is resized, the logo goes flying everywhere in what is obviously a bug. Especially if it has been running a long time, changing the modulus will result in random seeming jumps.
As another bonus you can make it non-linear, ie. having some basic non-linear physics.
John Carmack once pointed out, procedural generation is essentially just compression. Video playback only has time as state too.
This is just pointing out that if you have data to play back, all you need is a time variable for state. If you want to make that data procedurally (or somewhere in between by embedding a curve) you can do that too.
This does not have anything to do with programming in general.
[1]: https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResT... [2]: https://developer.mozilla.org/en-US/docs/Web/API/window/requ...
i i-1 i&i-1 from i|i-1 to
1 0 0 0 1 2
2 1 0 0 3 1
3 2 2 2 3 1
4 3 0 0 7 2
5 4 4 1 5 0
6 5 4 1 7 2
7 6 6 0 7 2His suggestion of starting with 1D was great, made it much easier to think about! I drew a graph of x against t, and worked out |t%(2w) - w| (starts rightmost) Can use absolute value, no need for ternary op. (In hindsight, graphing y against t would be easier to visualize.)
I'm always amazed at how cartesian coordinates are independent (despite being so by definition). Descartes deserves more credit for this eponymous fundamental insight.
For those thinking "simulation without state" (aka "procedural") only works for simple simulations, how about an ocean? The Technical Art of Sea of Thieves https://youtube.com/watch?v=y9BOz2dFZzs SIGGRAPH https://history.siggraph.org/learning/the-technical-art-of-s...
It's based on Simulating Ocean Water (Tessendorf 2001).
They do a similar thing with ropes drooping correctly, though it's really crazy the numerical approach they developed. Those guys are really smart.
I did an audio space animated thing using the canvas bouncing ball, and then I wanted to add arbitrary obstacle with lines at are at 90 or 180 degrees.
The math gets a magnitude more complicated immediately. Changing the angle of the ball doesn’t do that, but taking the walls off of the coordinate system does.
You go from simply adding a positive or negative signed number to some other situation where you have to calculate the vectors of all the bodies involved.
I’ve always suspected there’s some elegant solution that I’m missing.
Seriously? I knew js was a backward programming language but is there no seeded random function?
I hate these hot takes. Maybe you need state because sometimes things have state.