How do you make a part vibrate?

The title pretty much says it all.

So, how do I make an anchored part physically vibrate by changing it’s cframe? I know how to set a cframe of a part, but I don’t know how to vibrate a part.

2 Likes

Simplest way is to give it a sinusoidal displacement, along some axis. For example, you might have something like:

newCFrame = CFrame + A * math.sin( W * t ) * DirectionVector3

Where:
CFrame is the position of the object at rest (center of the vibration)
A is the amplitude of vibration (how far it moves side to either side of center) in studs
W is the frequency of vibration in Radians per second (not exactly how many times it vibrates per second, see note below)
t is time, like from tick() or from Stepped’s game time, or from accumulating the dt parameter of Heartbeat
DirectionVector3 is a unit Vector3 (length 1 stud), which is the axis along which the object moves back and forth while vibrating.

If you want to the frequency of vibration in Hertz (times per second), you have to set W = 2 * math.pi * F where F is the frequency in Hz. This is because one cycle of math.sin happens over 0 to 2pi radians, not 0 to 1.

This is simple harmonic motion. For more complex vibrations, you could add terms to this, using different values of A, W and Direction.To go even crazier, you can put the same sine wave expression into the angle argument of CFrame.fromAxisAngle( axis, angle ), then you will get rotational vibration about the provided axis.

If you want really chaotic vibration, equivalent to lots of random harmonics, replace math.sin with a call to math.random() instead. Like newCFrame = CFrame + A * (2 * math.random() - 1) * DirectionVector
No time parameter needed, the offset will just be some random displacement up to A studs in either direction from center.

Note that the term I just called “CFrame” is constant. It’s always the starting position of the object, not the result of the previous frame’s calculation.

Correction: I realized after I typed this out that I meant to put t in the math.sin() call, not dt. I’m just used to doing so many things incrementally that I derped that as I typed. Sorry!

*Disclaimer: Use of math.random() in the example above does not constitute my approval of using math.random(), you should of course use Random.new() and GetNextNumber(), but math.random() inlines nicer and I didn’t want to distract from the example by preaching my love for the Random class.

18 Likes

Here is an example file, with one block vibrating on its RightVector, and the other one vibrating angularly on its UpVector as the axis.Just hit the green Play button to see them do their thing using dead simple server Scripts on the parts.

Vibration.rbxl (18.3 KB)

4 Likes

I heard in a talk once, math.random gives rather rough vibrations and sometimes, even obnoxious, since the outputs don’t really have any relation to each other. It may appear as though you’re moving the pet from it’s position instead of displacing it.

And yes, there are some ways to smoothen it by tweaking the outputs. But wouldn’t you prefer using math.noise instead? I think since each output has a relation to the next, it’d give quite a smooth-er random vibration. I have no idea if it’ll work :sweat_smile: , but in theory it should.

2 Likes

Basic explanation:

Use a loop to change the CFrame using math.sin along the x axis like so

while wait() do -- whichever you decide
    part.CFrame = CFrame.new(math.sin(2), math.sin(1), math.sin(2))
end

Many different ways of achieving this. Experiment until you find one you like.

2 Likes

Why do we have to use math.sin() specifically there?

Is it required to do this? or can we just use normal numbers, why not other math functions too?

I’m not sure why you are using math.sin() here and we could use math.tan and math.cos too? since we could just get the normal CFrame numbers of the location, why specifically math.sin()?

Also worth noting that math.sin() returns the number in radians.

And please everyone avoid using while wait() do because it’s bad practice go to this to know why:

The While-Wait-Do Idiom, by cntkillme: Addressing while wait() loops

Instead you can use RunService’s Heartbeat Event as an alternative.

2 Likes

Use TweenService

local TweenService = game:GetService("TweenService")
local TweenSettings = TweenInfo.new(0.01, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut, 0, true, 0)

local function CreateVibration(Part, Duration)
    for i = 1, Duration do
        local RandomX = math.random()
        local RandomY = math.random()
        local RandomZ = math.random()
        --math.random by default gets a number between 0 and 1
        local VibrationVector = Part.Position + Vector3.new(RandomX, RandomY, RandomZ)

        local Tween = TweenService:Create(Part, TweenSettings, {Position = VibrationVector})
        Tween:Play()
        Tween.Completed:Wait()
    end
end

CreateVibration(workspace.Part, math.huge)
1 Like

As I said,

while wait() do is not bad practice. It is the same as while true do.

1 Like

Please read the thread by @colbert2677 which explains why you shouldn’t use while wait() do that I also linked in my previous post, it’s bad practice and you should always avoid using it.

You could use :GetPropertyChangedSignal() or the .Changed Event or RunService’s Events as an alternative, you are spreading misinformation and encouraging people to always use a bad practice which are both very bad…

1 Like

I read it long ago and it clearly states theres little no difference.
In my experience while true do often bugs and delays the thread in roblox lua.
Stop calling it bad practice, it isn’t.

The biggest misinformation is you repeating that it is.

1 Like

No it isn’t. It isn’t “bad” practice, but it’s inefficient.

It was proven that doing

while true do
    wait()
end

was faster than doing

while wait() do
end

I’ve done this experiment for you - I ran the following code:

local sT, c = tick(), 1
while wait() do
	c = c + 1
	if c > 100 then
		break
	end
end

print(tick() - sT)

sT, c = tick(), 1
while true do
	wait()
	c = c + 1
	if c > 100 then
		break
	end
end

print(tick() - sT)

Here are my results:

A - 4.8323 seconds
B - 3.3330 seconds

By doing it correctly, you’re shaving well over a second of off this loop. And this compounds - if it was a loop of a thousand, I’d save probably to the order of 10 seconds.

I don’t know what on earth you mean by “while true do bugs” - I’ve seen no evidence of this ever.


Also, why are you doing math.sin ? - I don’t see any use of static trigonometry here - you’re also not changing the rotation of the part (just the position), and since CFrame is an absolute (world) position, and you’re not doing any random usage, your code won’t function at all


@DragRacer31 Here is some code which will do what you want

local Generator = Random.new()
local OriginalCFrame = part.CFrame
while true do
    wait(0.1)
    part.CFrame = OriginalCFrame * CFrame.new(Generator:NextNumber(), Generator:NextNumber(), Generator:NextNumber())
end

It uses a Random number generator to provide random vibrations around from the part’s position - but it won’t slowly drift away as it remembers the original part’s Coordinates.

4 Likes

In my own experience while true has delayed for me, when I use while wait() it went smoothly so I’m not sure

Turns out that, whilst for larger values of n the difference is statistically insignificant (variance ±0.01), the initial values for while wait() fluctuate / spike (statistically significant). It falls roughly between the O(log(n)) and O(n) time function (not ideal, not too bad either).

Honestly, the difference is minor - and using wait() at all is generally seen as bad practice (see Avoiding wait() and why or “Using Wait Wisely” by Erik Cassel). That’s why I didn’t say it’s bad practice.

In this particular case though, using wait seems unavoidable - or at least the most convenient. RunService:BindToRenderStepped might be a better solution though.

1 Like

These results highly fluctuate and are dependant on the game, not to mention it also depends on how you use it.
At times i got ~3.7 seconds with while true do wait() and ~3.3 seconds with while wait() do.
I’m sorry, but did you make sure that your results are stable by running it several times?

I also ran this:

local sT, c = tick(), 1
while wait() and c < 100 do
	c = c + 1
end

print(tick() - sT)

and

local sT, c = tick(), 1
while true and c < 100 do
    wait()
    c = c + 1
end

print(tick() - sT)

and while wait() won by approximately 0.4 seconds every time.

This is a very bad and unstable example; there isn’t enough stable proof that while wait() do is indeed slower.

In my opinion arguing about which looping method is better is child-like and barely has any impact on the game, unless there is evident proof against such.
So far I have only seen one reason against while wait() do, which is that people often do

while wait() do
    if x > y then
        break
    end
end

instead of

while x < y do
    wait()
end

but by going with this argument, didnt you just contradict yourself by not doing as such?

1 Like

Yes - I ran the script three times on a linear increase and had the same results. The initial spike was replicated each time. What matters isn’t the exact time (which will change according to processor demand), but the trend line.

It is conceivable that the spike is only happening on my computer - however I found that it only happened with the while wait() and not while true each time. This lead me to believe it meets the threshold for statistical significance.

I’m arguing about this as a technicality. I’ve already suggested better alternatives to using while wait() or while true loops, as these are not good practice to implement in most situations.

My guess as to the issue is compile-time optimisation - the Lua compiler can’t simplify the instruction for the loop to be an infinite loop, instead it has to compare the function call from wait() each time to establish if the loop should continue, causing that variance.

2 Likes

while true do is compiled as a single jump whilst while wait() do is compiled as a test and a jump, which can thus lead to some minor drawbacks at the start, but other than that there’s nothing else.

From my understanding there is a difference between yielding and checking return contents. Doing a comparison operation every iteration will add up.

while (wait() == true) do -- has to establish wait() is truthy

vs

while true do

At the assembly level, I assume that the top one can be optimised to be jmp, whilst the comparison will have to perform a branch comparison?

I’ll have to actually compile it to bytecode to understand if this is the case, but I have the evidence (see above) that this is likely, as well a plausible hypothesis. It doesn’t even matter except at great scale or accuracy.

image

Imagine it like a diagram;
the first graph starts at origo (zero) and goes from there - thats while true do.
the second graph starts at a higher value than origo due to test, but it still increases by the same amount - thats while wait() do.

I barely know anything regarding VM instructions, so this is probably incorrect.
Don’t take my word for it :slightly_smiling_face:

while true vs while wait() is kind of moot here, since using a while loop to animate a part is already a bad practice. While loops will not resume if any error is thrown from within the loop body. This is a common source of bugs where a developer notices something in their game “just stops working after a while”. Your main game loops should also never be in infinite while loops, for the same reason.

The best practice is to update the part vibration on a RunService event, not in any kind of loop, because this will continue working for the life of the server (or client session).

1 Like

It will be rough. math.random() is white noise, so it’s not modeling continuous motion, it’s giving your part a position as a probability function (uniform along some line segment in space, in the case of my example). But… Roblox is only rendering (normally) at 60 Hz, so a single sine function with a frequency significantly above 30 Hz is going to look pretty much random too, especially if it’s not a multiple of 30 Hz and in sync with rendering. If you want the direction of motion, and the sinusoidal nature of the vibration to be perceivable, the vibration frequency should be well below 30 Hz. If you want vibration to be maximum (30 Hz), just use a boolean to toggle between two positions every other frame. Vibrations faster than 30 Hz are not possible to show at a 60 Hz rendering rate for the obvious reason that alternating between two positions is the fastest discretely-representable periodic oscillation.

2 Likes