Sign in to follow this  

Explosive tangent impulses causes extreme rotation

Recommended Posts

Hi there,


i got my html5/javascript 2D Sequential-Impulse based physics engine working - yay - but got issues with some weird behaviours :-(

It looks stable, but contacts creates explosive tangent impulses which increases the angular velocity.


Here are a video of some stacking circles which shows the weird effect (after forcing the collapse, the circles rotation goes faster and faster.


Another video showing the issue on a smaller scale:


I nearly use the same approach as box2d - except i added the position correction directly to the velocity bias. The friction code is identical and should be a direct port with one minor difference: i calculate the tangent normal once in the contact initialization - because the normal does not change for each contact/frame.


Source for contact solver + initialization:


No idea what causing these problems. I also tried to use a static/dynamic friction method, but had some more worse effects.


Would be really great, if you can help me to identify the issue.


Thanks in regards,


Share this post

Link to post
Share on other sites

I added simple contact force visualization which shows the final accumulated normal and tangent impulses.


See the following videos:


Should be able to compare your stuff with Box2D Lite.


I have already compared the solver stuff and have nearly an identical port now, but with some small differences:



                contact.velocityBias = this.baumGarte * dtInv * Math.min(0.0, contact.penetration + this.allowedPenetration);

my (added negative sign + penetration are switched):

                contact.velocityBias = -this.baumGarte * dtInv * Math.min(0.0, -contact.penetration + this.allowedPenetration);

Maybe my contact generation is not correct? For this case, i also created a video to show the contact generation. I love creating videos :D

Share this post

Link to post
Share on other sites

The circle-circle friction seems to be inverted. In the first video you can see, when the circle that gets pushed up falls onto the fast spinning circle, it starts to turn in the wrong direction,

Share this post

Link to post
Share on other sites

After analyzing box2d lite again, i added normal visualization and as i suspected, all normals are always the correct surface normals from the surface.

In my case - sometimes its correct, sometimes its not and i has to do with my contact generation.


For example, a simple halfspace and circle contact is created in my system like this:


- Get the distance vector between any point on the halfspace (in my case this is the halfspaces center - because its defined as normal * -distance) and the position of the circle.

- Now make a vector projection on the planes collision normal against this distance vector and invert the result

- If this projection is greater than the radius - we create a contact, otherwise not

- Contact point is circle position + planes collision normal * negative projection.

- Penetration is circle radius - projection

- The result contact normal is the inverted plane collision normal (If i use the actual plane normal, the relative velocity projection fails in the solver and the circle falls through the plane. (Even with the basic impulse like this: j = -(1.0 + restitution) * velAlongNormal )


I am not sure, what i am doing wrong here.


I also double checked my broadphase system which do simply two for loops, the outer goes from zero to the body count. The inner goes from the outer + 1 to the body count - These should create unique pairs always - of course static vs static are skipped as well.


Secondly i run the actual contact generation function from a jump table based on the bodies shape type:

        function flipContacts(list, count) {
            if (count > 0) {
                for (var i = list.size()-1; i > list.size()-1-count; i--){
                    var contact = list.item(i);
                    math.vec2MultScalar(contact.normal, contact.normal, -1);
                    var tmp = contact.bodyA;
                    contact.bodyA = contact.bodyB;
                    contact.bodyB = tmp;
            return count;

        function createContactCircleToHalfSpace(a, b, list) {
            var aDef = a.shape;
            var bDef = b.shape;

            var normal = bDef.normal;
            var pos = a.position;
            var pC = b.position;

            var sub = Vec2Pool.get();
            math.vec2Sub(sub, pC, pos);
            var proj = -math.vec2Dot(normal, sub);
            if (proj >= aDef.radius) {
                return 0;

            var contact = list.add();
            contact.bodyA = a;
            contact.bodyB = b;
            contact.penetration = aDef.radius - proj;
            math.vec2MultScalar(contact.normal, normal, -1);
            math.vec2AddMultScalar(contact.contactPoint, pos, normal, -proj);
            return 1;

        function createContactHalfSpaceToCircle(a, b, list) {
            return flipContacts(list, createContactCircleToHalfSpace(b, a, list));

        var contactFunctions = [];

        function addContactFunction(typeA, typeB, func) {
            if (typeof contactFunctions[typeA] == "undefined") {
                contactFunctions[typeA] = [];
            contactFunctions[typeA][typeB] = func;

        addContactFunction(ShapeType.Circle,ShapeType.HalfSpace, createContactCircleToHalfSpace);
        addContactFunction(ShapeType.HalfSpace,ShapeType.Circle, createContactHalfSpaceToCircle);

As you can see, the createContactHalfSpaceToCircle method is called when, bodyA is a halfspace and bodyB is a circle (inverted implementation of createContactCircleToHalfSpace) - therefore i flip the generated contacts (bodyA is bodyB and vice versa + invert the contact normal)

Edited by Finalspace

Share this post

Link to post
Share on other sites

k i think i need to start this from the beginning, therefore i made a really simple one-file javascript demo which shows 2 circles and a plane, with one iteration and accumulation disabled + no tangent impulses or rotations whatever. Seems to be working fine, but the contact normal for plane against circle is just wrong - i need to invert the plane normal as resulting contact normal - otherwise the circle falls through the plane. Why does this happens?

Edited by Finalspace

Share this post

Link to post
Share on other sites

i fixed the flipped plane normal issue - the plane needs to be always the first body, not the circle - the relative velocity was negative all the time.

But now the bodies sinks into each other over time - even its now a port of box2d lite with much simpler geometry.


Here you can see the sinking (Takes a while to take place):

Edited by Finalspace

Share this post

Link to post
Share on other sites

I recommend starting from Box2D *Lite*. Then you can start playing around from a working solution. Regarding the changes you made to Box2D I wonder what problem you try to solve and where you think your solution is better. I personally would only make such changes if it solves a particular problem while not breaking anything else.

Share this post

Link to post
Share on other sites

Its just i want to learn how a physics engine really works (i have bought several books for this), what are the maths behind and understand it in a simple way, so that i can implement it myself. In the last couple of months i looked deep in the box2d and box2d lite source nearly every day, tried to understand and visualize it - if i can visualize something - i can implement it. In the last few years i got highly interested in creating physics simulations, therefore i have read tons of papers, several books, tutorials - to learn all the math/methods behind it and now i am in the process of creating things - of course my final goal is to create a game based on that. But i think you are right, i think i will port box2d lite to javascript (dont like the existing ports) and expand it to my needs.

Share this post

Link to post
Share on other sites

Don't get too discouraged, if you are doing it for the purposes of learning a physics engine, I think the current path you are heading down is completely fine, and a little frustration and debugging are normal and part of the process.


On the other hand, if your desire is to just get the game part started, then I'd probably just expand box2d lite.

Share this post

Link to post
Share on other sites

Oh it seems i fixed it. The relative velocity for angular rotation was incorrect - now it works :-)


Original box2d lite:


// Relative velocity at contact
dv = b2->velocity + Cross(b2->angularVelocity, c->r2) - b1->velocity - Cross(b1->angularVelocity, c->r1);
my old impl:
                    var tempVec = Vec2Pool.get();
                    var rva = Vec2Pool.get();
                    var rvb = Vec2Pool.get();
                    math.vec2CrossSV(tempVec, bodyA.angularVelocity, contact.rA);
                    math.vec2Add(rva, bodyA.velocity, tempVec);
                    math.vec2CrossSV(tempVec, bodyB.angularVelocity, contact.rB);
                    math.vec2Sub(rvb, bodyB.velocity, tempVec);
                    math.vec2Sub(relVelocity, rvb, rva);
new working impl:
                    var tmp = Vec2Pool.get();
                    math.vec2CrossSV(tmp, bodyB.angularVelocity, contact.rB);
                    math.vec2Add(relVelocity, bodyB.velocity, tmp);
                    math.vec2Sub(relVelocity, relVelocity, bodyA.velocity);
                    math.vec2CrossSV(tmp, bodyA.angularVelocity, contact.rA);
                    math.vec2Sub(relVelocity, relVelocity, tmp);
Javascript does not support operator overload - therefore i needed to make this ugly code -.-

Share this post

Link to post
Share on other sites

Ah i am really happy right now, now i can go to the next step, implement contact caching + warm starting, better broadphase and finally polygon bodies.

If you want to see my progress, you can check my youtube channel frequently - i upload there things i have made - but mostly in javascript tongue.png


Thread can be closed.

Edited by Finalspace

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this