Typewriter Module

About the Module

After making a thread about my typewriter and getting a great answer I thought “why don’t I just upgrade it for use so anyone can use it easily” so here it is.

This module will individually write each letter in linear until the whole message given is wrote in the time requested, the module can play a sound for every letter written too. There’s also functions to pause and stop if ever needed.

When playing the typewriting while it is typewriting another text it shall completely stop the other process for the new process - that was mainly what my thread was about.


Typewriter.new(textObj : Instance,  yielding? : boolean, sound? : Instance)

Returns a writeObject for the textObject given, will be yielding if specified and shall play the sound if passed.

writeObject(text : string, timeToWrite : number, color? : Color3) -> boolean

If paused then will resume, otherwise starts typewriting to the textObject, in the time given.
Will set the TextColor3 to the color if given.
Returns if it completed writing without being overridden or stopped.

writeObject:play(text : string, timeToWrite : number, color? : Color3) -> boolean

Does the same as calling the writeObject (see above)


Pauses the typewriting, until played again.


Unpauses the typewriter and completely stops it.

writeObject:setYielding(bool : boolean)

Sets if the typewriting should yield.

writeObject:setSound(sound : Instance)

Sets the typing sound that should play.

writeObject:setTextObject(textObj : Instance)

Sets the Text object that it should typewrite upon.

Where you can get it

Either by getting it from the website or by requiring it’s id (require(4591774729))

Special thanks

A thanks again to @WoolHat for the WriteRequest idea.



  • Now supports foriegn characters and emojis (thanks to @sircfenner for telling me about this)
  • Select what color to write the text in
  • setYielding, setSound and setTextObject to edit the individual settings after the writeObject was made
1 Like

That means we could make something like a Camping Game Dialog Style with this Module? :o


You sure can!
For example:

local typewriter = require(4591774729)
local dialog = "Woah look at this amazing module I found in the woods!"
local writeAsync = typewriter.new(script.Parent)
writeAsync(dialog, 3)

You’re using wait without accounting for DeltaTime

	self.Function = function(text, timeToWrite)
		self.WriteRequest = self.WriteRequest + 1 
		local thisRequest = self.WriteRequest
		local interval = timeToWrite/utf8.len(text)
     	self.TextObj.Text = ""

	   	for start, finish in utf8.graphemes(text) do	
			if self.Paused.Value then self.Paused.Changed:Wait() end
			if thisRequest ~= self.WriteRequest then return false end 
	    	self.TextObj.Text = self.TextObj.Text .. string.sub(text, start, finish)
			if self.Sound then self.Sound:Play() end
			if thisRequest ~= self.WriteRequest then return false end
		return true

Without accounting for DeltaTime, when wait takes longer to resume, the text will take longer to appear. Even when you do account for DeltaTime, the text will appear in larger intervals as wait can take longer to resume. Using a Heartbeat might be better for this.

Here is a tutorial about this:


I wasn’t sure on how I could incorprate this completely, sometimes it wouldn’t fully write the text.
However, would this be ok?

local function wait(n)
    n = tick() + n
    until tick() >= n

That wouldn’t solve the problem entirely. How fast Heartbeat fires is dependent upon frame rate, so a slower frame rate would lead to longer yields. It also would still be slower than the intended time, as every yield will take slightly longer than the specified time.

Something like this should work:

	self.Function = function(text,timeToWrite)
		local thisRequest = self.WriteRequest + 1
		self.WriteRequest = thisRequest
		local len = utf8.len(text)
     	self.TextObj.Text = ""
		local startByte,utf8Byte,elapsed = 0,0,0
		while utf8Byte < len do
			if self.Paused.Value then self.Paused.Changed:Wait() end
			if thisRequest ~= self.WriteRequest then return false end
			elapsed = elapsed + Heartbeat:Wait()
			if self.Sound then self.Sound:Play() end
			local Nextutf8Byte = math.min(math.floor(elapsed/timeToWrite*len),len)
			if utf8Byte ~= Nextutf8Byte then
				startByte = utf8.offset(text,Nextutf8Byte-utf8Byte+1,startByte > 0 and startByte or 1)
				self.TextObj.Text = Nextutf8Byte == len and text or string.sub(text,1,utf8.offset(text,2,startByte)-1)
				utf8Byte = Nextutf8Byte
		return true