First, you should never, never, have an endless loop running without any wait, whatsoever. I don't know how your studio didn't crash when you ran this. Instead, you should think about what your situation actually requires. Does the script really need to check if it changed every millisecond? Or would it be more logical to check every time the value of buy changes? I think the former.
Let's start with that. To make this work, we have to utilize the .Changed event. Note: you can also use :GetPropertyChangedSignal() to serve the same purpose. I'll have links to both at the bottom of my post.
The .Changed event can be run on any instance. In your case, it will be what I assume is a TextBox, TextLabel, or TextButton.
local buy = script.Parent
buy.Changed:Connect(function(propertyThatChanged)
if propertyThatChanged == "Text" then
script.Parent.Parent.money.Text = tonumber(buy.Text) * 2
end
end)
Now, let's go over the reason that your script did not work, or rather, did not work as you intended it to (besides the game-killing while loop!)
When you declared buy, you defined it as script.Parent.Text. This is a very important concept. You saved the value of the Text property to the variable buy. When you did this, it was recorded there, rather permanently. This is not to say that is a static-like variable, as you could obviously redefine buy later. However, if you ran your script, went into the explorer, and changed buy's text, the variable's value would NOT change!
So keep this in mind in the future. Unless you explicitly want to store the value of that property at the given time, do not do it that way.
Feel free to comment with any further questions or concerns, and I hope this helped!
Resources:
GetPropertyChangedSignal()