<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.secondlife.com/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Becky+Pippen</id>
	<title>Second Life Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.secondlife.com/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Becky+Pippen"/>
	<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/wiki/Special:Contributions/Becky_Pippen"/>
	<updated>2026-05-12T19:22:52Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.42.1</generator>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Memory_Limits_FAQ&amp;diff=1152903</id>
		<title>User:Becky Pippen/Memory Limits FAQ</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Memory_Limits_FAQ&amp;diff=1152903"/>
		<updated>2011-09-02T18:11:53Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: This page will go away. If anyone want to take ownership of it, go ahead!&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;I won&#039;t be updating this page any more. If anyone would like to take ownership of it and maintain it, please do so. Otherwise this page will disappear soon. -- Becky&lt;br /&gt;
&lt;br /&gt;
== FAQ - Script Memory Limits ==&lt;br /&gt;
&lt;br /&gt;
For the most official announcement so far, see [https://blogs.secondlife.com/community/land/blog/2010/03/19/the-first-step-toward-script-limits here].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Why do we need script memory limits?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; In some regions, the simulator uses so much script memory that it has to swap virtual memory to disk. That causes major lag, and lag is among the top complaints about Second Life.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; How can I tell if I&#039;m a heavy script user?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Starting with server version 1.38 and viewer version 2.0, we can now see our individual script memory usage, similar to how estate managers can see top scripts in a region. Also scripters can use the function [[LlGetFreeMemory|llGetFreeMemory()]] to get an approximate idea of memory usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will this break content?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Yes, some. The limits are specifically designed to limit the worst 5% of memory consumers so that the remaining 95% can continue to run freely without causing the simulator to swap virtual memory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; When will all this happen?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Script memory reporting and the new memory-efficient LSL functions are now available on the main grid using server version 1.38 and version 2.0 of the SL viewer. Server version 1.40 or later will have &amp;quot;Small Scripts&amp;quot; capability (see below). Memory limits will not be enforced until later in 2010 after these features have been deployed on the main grid for some time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will the limits be per avatar or per region?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; There will be limits on avatar, parcel, and region in a way that parallels [[LSL_http_server#Resource_Limitations|how URL resources are managed]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will Linden Lab pay for all the scripted products we bought that will no longer work?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Most products are reasonably scripted and will continue to work just fine. The only problematic products will be unusual ones that are scripted in ways that use unreasonable amounts of RAM. Try to get the product creator to make a new version that uses less memory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will we see a reduction in lag across the grid?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; It won&#039;t have a major impact on most regions. However, it will make a huge improvement on the 5% of the regions that use so much script memory that the server has to swap virtual memory to disk. It is also expected to improve performance on any other simulators running on the same server host using the same disk resources.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; How much RAM are we talking about?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; In the 95% of the regions that run smoothly, the simulator uses less than 800MB of RAM for everything except scripts, and up to 300MB for all the scripts in the region, and that&#039;s ok.  In the worst 5% of the regions, simulators use up to 2GB of RAM on the server, and that&#039;s not ok.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Why not just add RAM to the servers? RAM is cheap.&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Several thousand Xeon server boards at, say, US$100 each to upgrade, comes out to a bunch of money, even for Linden Lab. Moving forward, new simulators (class 7) will have more RAM.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Who will be affected?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; All scripts, old and new, will be subject to the new memory limits. When planning for the memory limits, [http://wiki.secondlife.com/wiki/User:Babbage_Linden/Office_Hours/2009_11_25 Babbage Linden said], &amp;quot;[2009/11/25 3:43]  Babbage Linden: ideally we&#039;d like to have 95+% of people use scripts as they currently do while still having 95+% of simulators running without swapping&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What&#039;s the difference between &amp;quot;Small Scripts Project,&amp;quot; &amp;quot;Big Scripts Project,&amp;quot; and &amp;quot;Efficient Scripts Project?&amp;quot;&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; The &#039;&#039;Efficient Scripts Project&#039;&#039; is the overall effort to reduce the memory consumption of Mono-based scripts, including limits on memory usage. The &#039;&#039;Small Scripts Project&#039;&#039; and &#039;&#039;Big Scripts Project&#039;&#039; both refer to a refinement where each script can request the specific amount of memory it anticipates using instead of being charged with a constant 16KB for LSL or 64KB for Mono scripts. So, for example, a simple Mono sit script might request only 3KB while another data-intensive Mono script might request several megabytes. Since the average Mono script in SL needs only about 9KB, this could help reduce overall script memory usage in a typical region. In [http://wiki.secondlife.com/wiki/User:Babbage_Linden/Office_Hours/2010_03_03 Office Hours], Babbage Linden said, &amp;quot;I would like to ship script usage, then small scripts, then script limits, then big scripts... so it would be nice to have mono scripts be able to reserve a lower memory amount before script limits enforcement happens.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What&#039;s the &amp;quot;LSL Penalty?&amp;quot;&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; At one time, LL considered charging LSL scripts with more memory than they actually consume as an incentive for scripters to use Mono. That idea has been dropped, and the current plan is to charge all scripts with the actual memory they reserve — currently 16KB for LSL, and 64KB for Mono scripts. In the future after the Small Scripts and Big Scripts Project have been implemented, Mono scripts can be charged for only the amount of memory they reserve, which could be more or less than 64KB. Discussion of this can be found in the [http://lists.secondlife.com/pipermail/opensource-dev/ opensource-dev] mailing list.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What can I do as a content creator to prepare?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Be kind to your customers and give them product updates using the [[User:Becky_Pippen/New_LSL_Functions| new efficient LSL functions]]. Learn more about how to [[User:Becky_Pippen/Measure_Script_Memory_Usage|measure memory usage]] and the [[User:Becky_Pippen/Script_Memory_Limits|scripting techniques for reducing memory]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What can I do as a customer?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; 95% of scripts are just fine and you don&#039;t need to do anything. If you are wearing hair, shoes, or other attachments with a script in every prim, then you can contact the creator and ask if there is a product update that uses the new LSL memory-efficient functions.&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen&amp;diff=1152902</id>
		<title>User:Becky Pippen</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen&amp;diff=1152902"/>
		<updated>2011-09-02T18:06:39Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Remove link to page of obsolete script timing measurements.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== About ==&lt;br /&gt;
I love helping new residents get addicted... er, I mean acclimated to Second Life. Here are some notes I&#039;ve picked up along the way that might be of help.&lt;br /&gt;
== SL Glossary ==&lt;br /&gt;
[http://www.inkwelle.com/glossary_nci.shtml Second Life&#039;s most complete glossary]&lt;br /&gt;
== What is Mono? ==&lt;br /&gt;
=== John and Mary ===&lt;br /&gt;
John and Mary are LSL scripters. John prefers the old-fashioned way of doing things, while Mary enjoys all the latest new technology, like Mono. They both write LSL scripts. John runs his scripts under the old way, and Mary runs hers using Mono.&lt;br /&gt;
&lt;br /&gt;
When John writes an LSL script and clicks the Save button, the client viewer compiles the script into proprietary bytecode and uploads it to the servers. The server runs the script by using a proprietary interpreter to interpret the bytecode. It runs slow.&lt;br /&gt;
&lt;br /&gt;
Mary writes the identical script -- no change in the LSL language syntax. When she presses &amp;quot;Save&amp;quot;, the client uploads her script text to the servers where it gets compiled into standardized CIL assembly language. (CIL is a bytecode that originally came from Microsoft&#039;s .NET technology.) Because the Linden servers are Linux machines, there&#039;s no .NET framework  available to run the CIL code. So the servers use the open-source Mono framework to execute the CIL. Mono includes an open-source JIT (&amp;quot;Just in Time&amp;quot;) compiler that converts the CIL bytecode into machine code as it&#039;s needed, and that compiled machine code is what makes the script execute faster than the old way.&lt;br /&gt;
=== How it all Flows ===&lt;br /&gt;
Old way:&lt;br /&gt;
 LSL  ---&amp;gt; LSL compiler ---&amp;gt; proprietary bytecode ---&amp;gt; LSL bytecode interpreter&lt;br /&gt;
&lt;br /&gt;
New way:&lt;br /&gt;
 LSL                        ---&amp;gt; LSL compiler   | &lt;br /&gt;
 C# (future)                ---&amp;gt; C# compiler    |  ---&amp;gt; CIL bytecode ---&amp;gt; Mono CIL execution engine&lt;br /&gt;
 Other languages (future)   ---&amp;gt; other compiler |&lt;br /&gt;
=== Do Scripts Running in Mono Get More Memory? ===&lt;br /&gt;
Yes, it&#039;s true that each Mono script gets 64K of memory to use. Non-Mono scripts have to fit inside 16K of memory. (That&#039;s compiled code plus stack plus heap.) Unfortunately, Mono-compiled code is a bit of a memory hog and can take up to 4 times as much memory as the old runtime environment. So, scripts using Mono end up with a little more memory to play with depending on the code-to-data ratio, but not four times as much.&lt;br /&gt;
=== Evolving Terminology ===&lt;br /&gt;
LSL - Used to refer to the whole system -- the language, the front-end compiler, and the back-end interpreter. Now it&#039;s confusing because it&#039;s used in one of two ways and its meaning depends on the context:  (1) LSL is the language, the source code, which has not changed with Mono. E.g., &amp;quot;Take this LSL script and compile it with Mono and it will run faster.&amp;quot; Or (2) LSL refers to the old compiler front-end and interpreter virtual machine back-end. E.g., &amp;quot;When I compile this script with LSL it runs very slowly.&amp;quot; Both meanings are used in the sentence, &amp;quot;This LSL script was compiled with LSL, but that LSL script was compiled with Mono.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The term LSO refers to the bytecode generated by the traditional LSL compiler and executed by the old LSL runtime, and is a term that can be used to distinguish the old LSL tool set from the Mono tool set.&lt;br /&gt;
=== Can We Use Other Languages? ===&lt;br /&gt;
Mono just runs CIL bytecode. It doesn&#039;t know or care what the original language was. At this time (mid-2008) only LSL source code can be compiled into CIL and run on Mono. But maybe someday we will be able to compile C# and possibly other languages into CIL which will run under Mono. A single object could contain scripts that originally were written in different languages. &lt;br /&gt;
&lt;br /&gt;
The LSL language -- the source syntax -- will probably be supported for a very long time. &lt;br /&gt;
&lt;br /&gt;
However, the servers will support both back-ends for a very long time -- the one that executes the old proprietary LSL bytecode, and the new Mono virtual machine that executes standard CIL. All the millions of existing LSL scripts compiled the old way will continue to run indefinitely. New LSL scripts may be compiled using the old LSL compiler or the new Mono compiler.&lt;br /&gt;
=== What To Expect After Mono Hits the Main Grid ===&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;Old LSL scripts already compiled:&#039;&#039;&#039;&#039;&#039; will continue to run indefinitely. You don&#039;t have to do a thing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;Any LSL script if you have the source code:&#039;&#039;&#039;&#039;&#039; can be recompiled under the old LSL compiler or the new Mono compiler, at your choice, for a long time to come.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;C# and other languages:&#039;&#039;&#039;&#039;&#039; probably added at some future time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Animation Frame Rate ==&lt;br /&gt;
=== The Problem ===&lt;br /&gt;
You may have heard rumors that animations (BVH files) get reduced to 12 FPS when uploaded or played, and there&#039;s no need to make an animation faster than 12 frames per second (FPS). Other rumors say that fast animation frame rates give smoother animations, while other rumors say that you should use the lowest animation frame rate possible. Confused? Here&#039;s some information that might help.&lt;br /&gt;
=== The SL Viewer Interpolates ===&lt;br /&gt;
Part of the confusion is that the term &amp;quot;frame rate&amp;quot; means different things in different contexts. The SL viewer renders frames of the scene, including any animations in the scene, as fast as it can. You can always see how fast the client is rendering frames by looking for the FPS number in the Basic section in the Statistics Bar (Ctrl-Shift-1). The frame rate in an animation file is just there to establish the time scale of the animation information. It&#039;s a different kind of &amp;quot;frame.&amp;quot; So how do the two &amp;quot;frame rates&amp;quot; relate and how does it all work? It&#039;s simple -- when the viewer renders a frame, it determines how far along it is in the animation and interpolates the avatar&#039;s bone rotations based on the nearest two frames in the animation file.&lt;br /&gt;
&lt;br /&gt;
Refer to the following illustration to see how you can demonstrate this interpolation. Using your favorite animation editor, make an animation file consisting of just three or four frames. The first frame is the requisite reference frame, then frame 2 is the first key frame of the animation that defines the start of a movement. Frame 3 is the last frame of the movement. Optionally you can add a frame 4 that is a copy of frame2 to make a loop. Make a large movement between frames 2 and 3, such as arms straight down in frame 2 and straight up in frame 3. Set the animation for 1 frame per second. Upload the short animation file and set the looping parameters if needed so that the animation loops continuously.&lt;br /&gt;
&lt;br /&gt;
With the animation set to one frame per second, the arms should start at the sides, then one second later be raised above the head, then two seconds after the start of the animation, the arms should be back at the sides, then repeat. What happens if the client renders frames faster than one per second? If the viewer didn&#039;t interpolate, you would expect the avatar&#039;s arms to snap suddenly from one position to the other... snap up... snap down... etc. at one-second intervals. But that&#039;s not what happens at all. Regardless of your viewer frame rate, you&#039;ll see your avatar&#039;s hands moving smoothly, up and down, with all the intermediate positions between animation frames interpolated smoothly. To examine the effect in more detail, click Advanced-&amp;gt;Character-&amp;gt;Slow Motion Animations, then you&#039;ll have a whole bunch of interpolated rendered frames to examine.&lt;br /&gt;
&lt;br /&gt;
[[Image:Animation-interpolation.jpg|500px|||Viewer interpolates animation frames]]&lt;br /&gt;
&lt;br /&gt;
=== Asynchronous Frame Rates ===&lt;br /&gt;
In the following timeline example, the top lines represent the frames in an animation file, and the bottom lines show how fast the viewer renders frames and where the rendered frames correspond in time to the animation file. In this illustration, the animation frames are numbered starting at two, because frame one is the reference frame that never gets rendered. When the client is rendering the first frame of the animation (render frame 0 in the illustration), it corresponds to frame 2 in the animation file. In this illustration, the client is running at a fairly constant rate a little more than three times the rate of frames in the animation file:&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
animation frame number        2                         3                         4                         5&lt;br /&gt;
&lt;br /&gt;
animation frames:             K-------------------------K-------------------------K-------------------------K-----&lt;br /&gt;
&lt;br /&gt;
viewer rendered frames:       |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|&lt;br /&gt;
&lt;br /&gt;
viewer frame number:          0       1       2       3       4       5       6       7       8       9      10&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
When the viewer is rendering frame 0, the bone rotations exactly correspond to frame 2 in the animation file. After that, the time when a frame is rendered will almost always fall somewhere between two of the animation frames and the client will interpolate the bone rotations. For example, when the client renders frame 1, the bone rotations will be something between the rotations in frames 2 and 3 of the animation file, but closer to the rotations in frame 2. &lt;br /&gt;
&lt;br /&gt;
If the client is running slow in comparison to the animation file, then some data in the animation file must be skipped:&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
animation frame number        2     3     4     5     6     7     8     9    10    11    12    13    14    15 &lt;br /&gt;
&lt;br /&gt;
animation key frames:         K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K---&lt;br /&gt;
&lt;br /&gt;
viewer rendered frames:       |--------------|---------------|---------------|---------------|---------------|--&lt;br /&gt;
&lt;br /&gt;
viewer frame number:          0              1               2               3               4               5 &lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
In this scenario, when it&#039;s time for the client to render frame 1, the animation information is interpolated from frames 4 and 5 in the animation file, which means that any quick movement defined in frames 3 and 4 of the animation file will have been ignored that time around. If the animation loops, then perhaps on a subsequent loop the frames will align differently and a different set of animation frames will be ignored.&lt;br /&gt;
&lt;br /&gt;
=== Other Considerations ===&lt;br /&gt;
There are additional forces at work you need to be aware of:&lt;br /&gt;
&lt;br /&gt;
1. The higher the animation frame rate, the larger the file that has to be sent from the sim to every client in viewing range of the animation. Smaller is less laggy.&lt;br /&gt;
&lt;br /&gt;
2. When you upload an animation, the viewer silently drops small movements. It does this by comparing each bone&#039;s rotation in two adjacent frames, then dropping any data for bones that don&#039;t move much. The exact algorithm is complicated, but in general the threshold is a few degrees in any axis from one frame to the next. If a bone&#039;s rotation changes from one frame to the next less than 3 or 4 degrees in all axes, it might be dropped. If the movement is more than about 10 degrees in any axis, it&#039;s likely to be retained. If an animation is made with a high frame rate, then the movement from frame to frame will be smaller and more movements will be dropped. If the animation frames are less frequent, then the movement between frames will be larger and more likely to be retained.&lt;br /&gt;
&lt;br /&gt;
3. When the viewer interpolates bone rotations from the animation file, it uses a linear interpolation between the nearest frames, which sometimes looks a bit robotic. A higher animation rate lets you specify movements in finer resolution and make the movement start slow, get faster in the middle of the movement, then slow down when it reaches its maximum extent. &lt;br /&gt;
&lt;br /&gt;
=== Summary ===&lt;br /&gt;
The last two forces mentioned above work against each other, so the challenge for animators is to find a balance. The animation needs to be made with a high enough frame rate to describe the movements the way you want them to appear, yet it needs to be as low a frame rate as possible to avoid having the client discard small movements on upload. It depends on the animation, but a good starting point might be around 10 FPS for the animation. Try a lower animation frame rate if the animation appears ok with fewer frames, relying more on the linear interpolation by the client to smooth it out. Use a higher animation frame rate if the linear interpolation doesn&#039;t look quite right and you need finer resolution to describe more complex or non-linear movements.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Script Memory Limits ==&lt;br /&gt;
Script Memory limits are coming in 2010! Here&#039;s some information to help you prepare;&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Limits_FAQ| FAQ about script memory limits]]&lt;br /&gt;
* [[User:Becky_Pippen/Measure_Script_Memory_Usage|Measuring your script memory usage]]&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Efficiency|Calculating memory needed and memory usage efficiency]]&lt;br /&gt;
* [[User:Becky_Pippen/Script_Memory_Limits| A checklist for scripters -- techniques for reducing memory usage]]&lt;br /&gt;
* [[User:Becky_Pippen/New_LSL_Functions| New LSL functions llGetLinkPrimitiveParams(), llSetLinkPrimitiveParamsFast() and more]]&lt;br /&gt;
* [[User:Becky_Pippen/Hashing| Scripting techniques for hashing data to conserve memory]]&lt;br /&gt;
* [[User:Becky_Pippen/Numeric_Storage| Scripting techniques for storing arbitrary binary data without lists]]&lt;br /&gt;
* [[User:Becky_Pippen/Text_Storage| Scripting techniques for compressing ASCII text in Mono]]&lt;br /&gt;
* [http://lslwiki.net/lslwiki/wakka.php?wakka=MemoryUsage Empirical measurements of memory usage by data type and scope (lslwiki.net)]&lt;br /&gt;
* [[LSL_Script_Memory|Empirical measurements of memory usage by data type and scope (SL wiki)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Shared Media-on-a-Prim LSL recipes ==&lt;br /&gt;
[[User:Becky_Pippen/Shared_Media_LSL_Recipes|See here]]&lt;br /&gt;
&lt;br /&gt;
==Cloud Be Gone!==&lt;br /&gt;
Tired of being a Ruth Cloud? See [[User:Becky_Pippen/CloudBeGone|this article]] for the technical reasons why&lt;br /&gt;
various solutions work or not.&lt;br /&gt;
&lt;br /&gt;
For fun, [[User:Becky_Pippen/RuthCloudScript|see here for a Ruth Cloud LSL script]].&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [http://jira.secondlife.com/ JIRA Issue Tracker]&lt;br /&gt;
* [https://wiki.secondlife.com/wiki/About_AWG Architecture Working Group]&lt;br /&gt;
{{skills&lt;br /&gt;
|Builder=*&lt;br /&gt;
|Scripter=*&lt;br /&gt;
|Terraformer=&lt;br /&gt;
|Architect=&lt;br /&gt;
|Scenographer=&lt;br /&gt;
|SLogistician=&lt;br /&gt;
|}}&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/LSL_Performance&amp;diff=1152901</id>
		<title>User:Becky Pippen/LSL Performance</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/LSL_Performance&amp;diff=1152901"/>
		<updated>2011-09-02T18:04:34Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: These old timing measurements are obsolete and this page will disappear soon.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;With the move to virtualized class 7 servers and changes to the Mono virtual machine, these performance&lt;br /&gt;
measurements made on old class 5 servers are no long very meaningful. Certainly the absolute timings are different now,&lt;br /&gt;
and probably the relative timings are different too. The generated CIL code shown below might still be the same&lt;br /&gt;
(I haven&#039;t checked), but that&#039;s about all that&#039;s left in this page that&#039;s relevant. So, this page is&lt;br /&gt;
obsolete and will disappear soon.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==LSL Timings and Rates==&lt;br /&gt;
Here are some selected LSL function times and event rates comparing scripts compiled and run using the old LSL toolset (a.k.a LSO), and using Mono.&lt;br /&gt;
&lt;br /&gt;
The basic test framework for these tests is a loop like shown below, where we take the difference in llGetTime() before and after the loop:&lt;br /&gt;
&lt;br /&gt;
 while (--i &amp;gt;= 0) {&lt;br /&gt;
     f(); f(); f(); f(); f(); f(); f(); f();&lt;br /&gt;
     f(); f(); f(); f(); f(); f(); f(); f();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The loop is partially unrolled to make the loop control structure overhead negligible. We take several measurements in multiple Class 5 sims that are running consistently with a total frame time under 10 ms, where each test runs for 30 - 120 minutes, or longer if we fall asleep during the test. We measure several times during a 24-hour period until the numbers converge with a variance of 10% or less.&lt;br /&gt;
&lt;br /&gt;
Measurements are taken in at least three regions, because we&#039;ve occasionally seen regions with lots of reported spare time per frame, yet scripts seem to run inexplicably slow. If two out of three regions converge and agree, the anomalous region is ignored. Consider the numbers reported on this page to be maximums possible in cooperative regions.&lt;br /&gt;
&lt;br /&gt;
In a Mono test, the result of the first run after script reset is ignored to eliminate the overhead of the VM initialization, JIT compilation, etc.&lt;br /&gt;
&lt;br /&gt;
The Mono CIL shown for some of the LSL code below was obtained by modifying an SL viewer so that it calls lscript_compile() when saving a script. (Scripts are compiled on the simulators nowadays, but Linden Lab has kindly left the script compiler code in the open source viewer source code.) Comments and formatting were added manually.&lt;br /&gt;
&lt;br /&gt;
Note that these are stopwatch times. How rapidly you can call a function or how often an event handler is invoked doesn&#039;t necessarily correspond to how much those functions and events lag a sim because of all the throttling and scheduling going on behind the scenes inside the run-time engine.&lt;br /&gt;
&lt;br /&gt;
Class 7 hosts are starting to appear on the main grid as reported [[Beta_Server_Office_Hours/Minutes/2010-02-04|here]] and [[User:Andrew_Linden/Office_Hours/2010_02_02|here]]. As Linden Lab moves toward classless regions, absolute timings like shown on this page will become less meaningful. Maybe we can make use of some sort of relative performance measurements in the future.&lt;br /&gt;
&lt;br /&gt;
== Functions, operators, &amp;amp; control structures ==&lt;br /&gt;
&lt;br /&gt;
===Floating point assignment===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|x = [[PI]];&lt;br /&gt;
where x is a global [[float]]&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.11&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.0007&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    .field public float32 &#039;x&#039;         // float x;&lt;br /&gt;
    . . .&lt;br /&gt;
    ldarg.0                           // push &#039;this&#039; pointer&lt;br /&gt;
    ldc.r8 (00 00 00 60 fb 21 09 40)  // push 64-bit PI&lt;br /&gt;
    stfld float32 LSL_script::&#039;x&#039;     // pop and store 32-bit x&lt;br /&gt;
    ldarg.0&lt;br /&gt;
    ldfld float32 LSL_script::&#039;x&#039;&lt;br /&gt;
    pop&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}  &lt;br /&gt;
:Notes: Verified and updated for server version 1.34.2.142087.&lt;br /&gt;
&lt;br /&gt;
===Call to empty f() { }===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|f();&lt;br /&gt;
where f() is defined as f() { }&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.33&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.0026&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
Function definition:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
 .method public hidebysig instance default void &#039;gf&#039;() cil managed&lt;br /&gt;
 {&lt;br /&gt;
     .maxstack 500&lt;br /&gt;
     ret&lt;br /&gt;
 }&amp;lt;/code&amp;gt;&lt;br /&gt;
Caller:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
     ldarg.0    // push &#039;this&#039; pointer&lt;br /&gt;
     call instance void class LSL_script::&#039;gf&#039;()&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[for]] loop overhead===&lt;br /&gt;
:This test measures the overhead of a [[for]] loop that iterates an empty block just once.&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[for]] (i = 0; i == 0; ++i) { }&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.07&#039;&#039;&#039; ms/loop&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.001&#039;&#039;&#039; ms/loop&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    // loop init:  i = 0&lt;br /&gt;
    ldc.i4.0      // push 32-bit 0&lt;br /&gt;
    stloc.s 0     // pop and store in i&lt;br /&gt;
    ldc.i4 0      // push 32-bit 0&lt;br /&gt;
    dup           // duplicate 0 on the stack&lt;br /&gt;
    stloc.s 0     // pop and store 0 in i&lt;br /&gt;
    pop           // cleanup&lt;br /&gt;
 &lt;br /&gt;
 LabelTempJump0:&lt;br /&gt;
    // top of loop: test i == 0&lt;br /&gt;
    ldc.i4 0      // push 32-bit 0&lt;br /&gt;
    ldloc.s 0     // push i&lt;br /&gt;
    ceq           // compare i and 0&lt;br /&gt;
    brfalse LabelTempJump1 // branch if i != 0&lt;br /&gt;
 &lt;br /&gt;
    // bottom of loop: ++i&lt;br /&gt;
    ldloc.s 0     // push i&lt;br /&gt;
    ldc.i4.1      // push 32-bit 1&lt;br /&gt;
    add           // pop i and 1, add, push result&lt;br /&gt;
    dup           // duplicate result&lt;br /&gt;
    stloc.s 0     // save result in i&lt;br /&gt;
    pop           // cleanup&lt;br /&gt;
    br LabelTempJump0&lt;br /&gt;
 &lt;br /&gt;
 LabelTempJump1:&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[State]] change===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[state]] a;&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.14&#039;&#039;&#039; ms/transition&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;22.2&#039;&#039;&#039; ms/transition&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0            // push &#039;this&#039; pointer&lt;br /&gt;
    ldstr &amp;quot;state2&amp;quot;&lt;br /&gt;
    call instance void class [LslUserScript] \&lt;br /&gt;
            LindenLab.SecondLife.LslUserScript::ChangeState(string)&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes:&lt;br /&gt;
:State changes in Mono are tied to the sim frame rate of 45 fps (per Vektor Linden). The test code for state changes is an outer loop around 16 state changes like this:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
::    . . .&lt;br /&gt;
::    state state&#039;&#039;N&#039;&#039;   { state_entry() { state state&#039;&#039;N+1&#039;&#039;; } }&lt;br /&gt;
::    state state&#039;&#039;N+1&#039;&#039; { state_entry() { state state&#039;&#039;N+2&#039;&#039;; } }&lt;br /&gt;
::    . . . etc.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===[[Vector]] operations===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|v = &amp;lt;PI,[[PI]],PI&amp;gt; + &amp;lt;PI,PI,PI&amp;gt; * PI;&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.43&#039;&#039;&#039; ms/statement&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.025&#039;&#039;&#039; ms/statement&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0  // push &#039;this&#039; pointer&lt;br /&gt;
    ldc.r8 (00 00 00 60 fb 21 09 40) // push 64-bit PI (vector x)&lt;br /&gt;
    ldc.r8 (00 00 00 60 fb 21 09 40) // push 64-bit PI (vector y)&lt;br /&gt;
    ldc.r8 (00 00 00 60 fb 21 09 40) // push 64-bit PI (vector z)&lt;br /&gt;
    ldc.r8 (00 00 00 60 fb 21 09 40) // push 64-bit PI multiplicand&lt;br /&gt;
    call class [ScriptTypes]LindenLab.SecondLife.Vector \&lt;br /&gt;
            class [LslUserScript]LindenLab.SecondLife.LslUserScript:: \&lt;br /&gt;
            &#039;CreateVector&#039;(float32, float32, float32)&lt;br /&gt;
    call class [ScriptTypes]LindenLab.SecondLife.Vector \&lt;br /&gt;
            class [LslUserScript]LindenLab.SecondLife.LslUserScript:: \&lt;br /&gt;
            &#039;Multiply&#039;(float32, class [ScriptTypes]LindenLab.SecondLife.Vector)&lt;br /&gt;
    ldc.r8 (00 00 00 60 fb 21 09 40) // push 64-bit PI&lt;br /&gt;
    ldc.r8 (00 00 00 60 fb 21 09 40) // push 64-bit PI&lt;br /&gt;
    ldc.r8 (00 00 00 60 fb 21 09 40) // push 64-bit PI&lt;br /&gt;
    call class [ScriptTypes]LindenLab.SecondLife.Vector \&lt;br /&gt;
            class [LslUserScript]LindenLab.SecondLife.LslUserScript:: \&lt;br /&gt;
            &#039;CreateVector&#039;(float32, float32, float32)&lt;br /&gt;
    call class [ScriptTypes]LindenLab.SecondLife.Vector \&lt;br /&gt;
            class [LslUserScript]LindenLab.SecondLife.LslUserScript:: \&lt;br /&gt;
            &#039;Add&#039;(class [ScriptTypes]LindenLab.SecondLife.Vector, \&lt;br /&gt;
            class [ScriptTypes]LindenLab.SecondLife.Vector)&lt;br /&gt;
    // save result in v:&lt;br /&gt;
    stfld class [ScriptTypes]LindenLab.SecondLife.Vector LSL_script::&#039;v&#039;&lt;br /&gt;
    ldarg.0  // push &#039;this&#039; pointer&lt;br /&gt;
    ldfld class [ScriptTypes]LindenLab.SecondLife.Vector LSL_script::&#039;v&#039;&lt;br /&gt;
    pop&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[llAbs]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llAbs]]();&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.41&#039;&#039;&#039; ms/statement&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.012&#039;&#039;&#039; ms/statement&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldc.i4 0 // push the integer argument&lt;br /&gt;
    call int32 class [LslLibrary]LindenLab.SecondLife.Library::&#039;llAbs&#039;(int32)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: verified and updated for server ver. 1.34.2.142087&lt;br /&gt;
&lt;br /&gt;
===[[Category:LSL_CSV|CSV]] [[string]] conversions===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llCSV2List]]([[llList2CSV]](x));&lt;br /&gt;
where x is a [[list]] of 10 [[integer]]s&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;3.2&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;4 - 7&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0     // push &#039;this&#039; pointer&lt;br /&gt;
    ldfld class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] LSL_script::&#039;x&#039;&lt;br /&gt;
    call string class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llList2CSV&#039;(class [mscorlib]System.Collections.ArrayList)&lt;br /&gt;
    call class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] class \&lt;br /&gt;
            [LslLibrary]LindenLab.SecondLife.Library::&#039;llCSV2List&#039;(string)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: So far, the Mono results vary too much for any meaningful result.&lt;br /&gt;
&lt;br /&gt;
===[[llDetectedName]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llDetectedName]]();&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;1.1&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.8&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldc.i4 0  // push argument&lt;br /&gt;
    call string class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llDetectedName&#039;(int32)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[llGetInventoryName]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llGetInventoryName]](INVENTORY_TEXTURE, 0);&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.52&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.21&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldc.i4 0    // push INVENTORY_TEXTURE&lt;br /&gt;
    ldc.i4 0    // push 2nd argument&lt;br /&gt;
    call string class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llGetInventoryName&#039;(int32, int32)&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[llGetObjectDetails]]===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llGetObjectDetails]](k, [OBJECT_OWNER]);&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;1.3&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;1.7&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0  // push &#039;this&#039; pointer&lt;br /&gt;
    // push k:&lt;br /&gt;
    ldfld valuetype [ScriptTypes]LindenLab.SecondLife.Key LSL_script::&#039;k&#039;&lt;br /&gt;
    ldc.i4 6 // push OBJECT_OWNER&lt;br /&gt;
    box [mscorlib]System.Int32 &lt;br /&gt;
    call class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] \&lt;br /&gt;
            class [LslUserScript]LindenLab.SecondLife.LslUserScript:: \&lt;br /&gt;
            CreateList()&lt;br /&gt;
    call class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] \&lt;br /&gt;
            class [LslUserScript]LindenLab.SecondLife.LslUserScript:: \&lt;br /&gt;
            Prepend(object, class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList])&lt;br /&gt;
    call class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] \&lt;br /&gt;
            class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llGetObjectDetails&#039;(valuetype \&lt;br /&gt;
            [ScriptTypes]LindenLab.SecondLife.Key, \&lt;br /&gt;
            class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: Verified and updated for server ver. 1.34.2.142087&lt;br /&gt;
&lt;br /&gt;
===[[llGetPos]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llGetPos]]();&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.39&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.025&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    call class [ScriptTypes]LindenLab.SecondLife.Vector \&lt;br /&gt;
        class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
        &#039;llGetPos&#039;()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: Verified and updated for server ver. 1.34.2.142087&lt;br /&gt;
&lt;br /&gt;
===[[llList2Vector]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llList2Vector]](x, 0);&lt;br /&gt;
where x = [ [[ZERO_VECTOR]] ]&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.55&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.012&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0   // push &#039;this&#039; pointer&lt;br /&gt;
    // push list x:&lt;br /&gt;
    ldfld class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] LSL_script::&#039;x&#039;&lt;br /&gt;
    ldc.i4 0  // push 2nd argument: 0&lt;br /&gt;
    call class [ScriptTypes]LindenLab.SecondLife.Vector \&lt;br /&gt;
            class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llList2Vector&#039;(class \&lt;br /&gt;
            [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList], int32)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[llMD5String]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llMD5String]](s,0);&lt;br /&gt;
where s is a 64-char [[string]]&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;1.4&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;1.4&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0                       // push &#039;this&#039; pointer&lt;br /&gt;
    ldfld string LSL_script::&#039;s&#039;  // push string s&lt;br /&gt;
    ldc.i4 0                      // push 2nd arg: 0&lt;br /&gt;
    call string class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llMD5String&#039;(string, int32)&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[llMessageLinked]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=3 width=520|[[llMessageLinked]]([[LINK_THIS]], 0, &amp;quot;&amp;quot;, [[NULL_KEY]]);&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}One consumer script:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;1.5&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.9&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}Two consumer scripts:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;1.8&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;1.2&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}Four consumer scripts:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;2.4&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;1.8&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}Eight consumer scripts:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;4.4&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;3.3&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}16 consumer scripts:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;9 - 20&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;7 - 15&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}32 consumer scripts:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;22.2&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;22.2&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=3|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldc.i4 -4    // push LINK_THIS&lt;br /&gt;
    ldc.i4 0     // push 2nd arg: 0&lt;br /&gt;
    ldstr &amp;quot;&amp;quot;     // push 3rd arg: &amp;quot;&amp;quot;&lt;br /&gt;
    ldstr &amp;quot;00000000-0000-0000-0000-000000000000&amp;quot; // push 4th arg as string&lt;br /&gt;
    // convert that last string to a key type:&lt;br /&gt;
    call valuetype [ScriptTypes]LindenLab.SecondLife.Key \&lt;br /&gt;
            class [LslUserScript]LindenLab.SecondLife.LslUserScript:: \&lt;br /&gt;
            &#039;CreateKey&#039;(string)&lt;br /&gt;
    call void class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llMessageLinked&#039;(int32, int32, string, valuetype \&lt;br /&gt;
            [ScriptTypes]LindenLab.SecondLife.Key)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: The time it takes to call llMessageLinked() is dependent on the number of consumers of the message, up to about 20-30 consumer scripts. At that point, the time to send a link message becomes constant at one call per 22.2 ms, which is suspiciously similar to the sim&#039;s basic frame rate.&lt;br /&gt;
&lt;br /&gt;
===[[llParseString2List]]===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llParseString2List]](s,a,b);&lt;br /&gt;
where s is 2000 random digits, a=[&amp;quot;0&amp;quot;,&amp;quot;1&amp;quot;]; b=[&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;];&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;45&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;24&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0                      // push &#039;this&#039; pointer&lt;br /&gt;
    ldfld string LSL_script::&#039;s&#039; // push 1st arg: s&lt;br /&gt;
    ldarg.0                      // push &#039;this&#039;&lt;br /&gt;
    // push 2nd arg: list a:&lt;br /&gt;
    ldfld class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] LSL_script::&#039;a&#039;&lt;br /&gt;
    ldarg.0                      // push &#039;this&#039;&lt;br /&gt;
    // push 3rd ard: list b:&lt;br /&gt;
    ldfld class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] LSL_script::&#039;b&#039;&lt;br /&gt;
    call class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] \&lt;br /&gt;
            class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llParseString2List&#039;(string, \&lt;br /&gt;
            class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList], \&lt;br /&gt;
            class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[llSay]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llSay]](-2, &amp;quot;0123456789&amp;quot;);&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.7&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.2&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldc.i4 2           // push 2...&lt;br /&gt;
    neg                // ...and negate it = -2&lt;br /&gt;
    ldstr &amp;quot;0123456789&amp;quot; // push 2nd arg&lt;br /&gt;
    call void class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llSay&#039;(int32, string)&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes:&lt;br /&gt;
:#The performance of llSay(-2, &amp;quot;0123456789&amp;quot;) varies widely by region, although it can be pretty consistent within a region. In some nearly empty regions, llSay() slows to no better than 5 ms/call for reasons unknown. It&#039;s as if the throttling for this function is less deterministic than other LSL functions. But even in the worst case, llSay() can transmit faster than llListen() can receive.&lt;br /&gt;
:#Verified and updated for server ver. 1.34.2.142087.&lt;br /&gt;
&lt;br /&gt;
===[[llSetLinkAlpha]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llSetLinkAlpha]](link, alpha, side);&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;1.0&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.52&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldc.i4 1                         // push 1st arg: link number&lt;br /&gt;
    ldc.r8 (00 00 00 60 b8 1e d5 3f) // push 2nd arg: float alpha&lt;br /&gt;
    ldc.i4 0                         // push 3rd arg: side number&lt;br /&gt;
    call void class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llSetLinkAlpha&#039;(int32, float32, int32)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[llSetText]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llSetText]](s,c,a);&lt;br /&gt;
where [[string]] s is 20 chars&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.79&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.38&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0                       // push &#039;this&#039; pointer&lt;br /&gt;
    ldfld string LSL_script::&#039;s&#039;  // push 1st arg: string s&lt;br /&gt;
    ldarg.0                       // push &#039;this&#039;&lt;br /&gt;
    // push 2nd arg: color:&lt;br /&gt;
    ldfld class [ScriptTypes]LindenLab.SecondLife.Vector LSL_script::&#039;c&#039;&lt;br /&gt;
    ldarg.0                       // push &#039;this&#039;&lt;br /&gt;
    ldfld float32 LSL_script::&#039;a&#039; // push 3rd arg: a&lt;br /&gt;
    call void class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llSetText&#039;(string, \&lt;br /&gt;
            class [ScriptTypes]LindenLab.SecondLife.Vector, float32)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[llSubStringIndex]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llSubStringIndex]](s, &amp;quot;X&amp;quot;);&lt;br /&gt;
where s is 10000 digits not containing &amp;quot;X&amp;quot;&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;16&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;22&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0                      // push &#039;this&#039; pointer&lt;br /&gt;
    ldfld string LSL_script::&#039;s&#039; // push 1st arg: string a&lt;br /&gt;
    ldstr &amp;quot;X&amp;quot;                    // push 2nd arg: literal string&lt;br /&gt;
    call int32 class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llSubStringIndex&#039;(string, string)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
== Event handlers ==&lt;br /&gt;
&lt;br /&gt;
===[[dataserver]]() for [[llGetNotecardLine]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|Maximum rate of [[dataserver]]() events using [[llGetNotecardLine]]()&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;6.4&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;6.4&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[link_message]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|Maximum rate of [[link_message]]() events&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;42-45&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;42-45&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: The results were highly variable over several tens of millions of link_message() events in three lightly loaded sims, but I wonder if the maximum rate is tied to the sim frame rate of 45 fps.&lt;br /&gt;
&lt;br /&gt;
===[[listen]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=3 width=520|Maximum rate of [[listen]]() events&lt;br /&gt;
any number of scripts per prim, any number of prims in linkset&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}1 listener per script:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;14.7&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;14.7&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}Two listeners per script:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;11&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;11&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}Three listeners per script:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;8.9&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;8.9&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[sensor]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|Maximum rate of [[sensor]]() events&lt;br /&gt;
after [[llSensorRepeat]]()&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;15&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;22&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: The maximum rate of sensor() events seems to be independent of the sensor radius or number of things detected.&lt;br /&gt;
&lt;br /&gt;
===[[timer]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|Maximum rate of [[timer]]() events&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;22&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;22&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: About half the sim&#039;s basic frame rate of 45 fps.&lt;br /&gt;
&lt;br /&gt;
===[[touch]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|Maximum rate of [[touch]]() events&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;22&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;22&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: About half the sim&#039;s basic frame rate of 45 fps.&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Measure_Script_Memory_Usage&amp;diff=1152313</id>
		<title>User:Becky Pippen/Measure Script Memory Usage</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Measure_Script_Memory_Usage&amp;diff=1152313"/>
		<updated>2011-08-24T19:14:54Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Add links to new LSL functions for measuring memory usage&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;News! We now have new LSL functions for measuring script memory usage, and&lt;br /&gt;
functions for setting lower memory limits. These were introduced in server&lt;br /&gt;
versions 11.06.17.233176&lt;br /&gt;
([[Release_Notes/Second_Life_Server/11#11.06.17.233176|release notes]])&lt;br /&gt;
and 11.08.10.238207&lt;br /&gt;
([[Release_Notes/Second_Life_Server/11#11.08.10.238207|release notes]]).&lt;br /&gt;
&lt;br /&gt;
These new functions include:&lt;br /&gt;
* [[llScriptProfiler]] - For Mono, enables runtime memory profiling&lt;br /&gt;
* [[llGetSPMaxMemory]] - Maximum memory used during profiling&lt;br /&gt;
* [[llGetUsedMemory]] - For Mono or LSO, current script memory usage&lt;br /&gt;
* [[llSetMemoryLimit]] - For Mono only, sets a memory limit lower than the default 64KB&lt;br /&gt;
* [[llGetMemoryLimit]] - Returns the current maximum memory usage limit &lt;br /&gt;
* [[OBJECT_SCRIPT_MEMORY]] - Use with [[llGetObjectDetails]]() to get an object or agent&#039;s total memory limit&lt;br /&gt;
&lt;br /&gt;
The old information about measuring memory usage with [[llGetFreeMemory]]() is shown below.&lt;br /&gt;
&lt;br /&gt;
=== Measuring Script Memory Usage ===&lt;br /&gt;
Scripters can use [[llGetFreeMemory]]() to make more detailed measurements of how specific functions or&lt;br /&gt;
statements affect memory usage.&lt;br /&gt;
The memory a script uses is:&lt;br /&gt;
* Starting memory (65536 bytes for [[Mono]], 16384 for [[LSO|LSL]])&lt;br /&gt;
* minus program statements   (e.g., a = 2;)&lt;br /&gt;
* minus static (pre-defined) variables  (e.g., integer a;)&lt;br /&gt;
* minus dynamic (created when running) data and overhead   (e.g., f(&amp;quot;a&amp;quot; + &amp;quot;b&amp;quot;) )&lt;br /&gt;
Here is an LSL statement that reports how much free memory you have at the moment it&#039;s called:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llOwnerSay((string)llGetFreeMemory() + &amp;quot; bytes free&amp;quot;);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To report how much memory is used (instead of free) in a Mono script, subtract&lt;br /&gt;
free from total memory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llOwnerSay((string)(65536 - llGetFreeMemory()) + &amp;quot; bytes used&amp;quot;);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For LSL-compiled scripts, change 65536 to 16384.&lt;br /&gt;
&lt;br /&gt;
It makes a difference where you place [[llGetFreeMemory]]() in your script. To see how&lt;br /&gt;
much memory is used by the script&#039;s program code and static (pre-defined) variables,&lt;br /&gt;
put [[llGetFreeMemory]]() as the first line in [[state_entry]]() or [[on_rez]]() in the default&lt;br /&gt;
state. To see how much memory your script used including all runtime dynamic data,&lt;br /&gt;
call [[llGetFreeMemory]]() as the last statement executed after your script has created&lt;br /&gt;
all the data it&#039;s going to.&lt;br /&gt;
&lt;br /&gt;
To see how much memory is used by a one or a few program statements,&lt;br /&gt;
measure the memory before and after and take the difference. Be aware that your&lt;br /&gt;
instrumentation for measuring memory might itself consume some memory&lt;br /&gt;
if it uses variables or function calls, so do all that prior to the measurement.&lt;br /&gt;
&lt;br /&gt;
Also be aware that the way the LSL and Mono back ends allocate memory (and&lt;br /&gt;
perhaps from fluctuations due to garbage collection), you might&lt;br /&gt;
get varying results from individual measurements, so it&#039;s sometimes useful to&lt;br /&gt;
take an average.&lt;br /&gt;
&lt;br /&gt;
For example, suppose we want to measure how much memory gets consumed each time&lt;br /&gt;
your script saves a short string to a global list. Here is the statement that we want to&lt;br /&gt;
measure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;aList += &amp;quot;hello&amp;quot;;  // appends one 5-char string to the list&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To measure the memory used by this statement, we&#039;ll average the memory consumption over a hundred loops.&lt;br /&gt;
We&#039;ll also globally declare the temporary variables we&#039;ll need for the measurement&lt;br /&gt;
so that between the start and end of the test, there will be no extra variables allocated&lt;br /&gt;
or function calls made except the code being measured:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
 list aList;&lt;br /&gt;
 integer startFreeMem;&lt;br /&gt;
 integer endFreeMem;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
        integer loops = 100;&lt;br /&gt;
        startFreeMem = llGetFreeMemory();&lt;br /&gt;
 &lt;br /&gt;
        while (--loops &amp;gt;= 0) {&lt;br /&gt;
            aList += &amp;quot;hello&amp;quot;;  // appends one 5-char string to the list&lt;br /&gt;
        }&lt;br /&gt;
 &lt;br /&gt;
        endFreeMem = llGetFreeMemory();&lt;br /&gt;
        llOwnerSay(&amp;quot;Change in free mem: &amp;quot; + (string)(startFreeMem - endFreeMem));&lt;br /&gt;
     }&lt;br /&gt;
 }&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
By the way, the results I got from this test shows that it takes 3486 bytes of memory to&lt;br /&gt;
append 100 copies of &amp;quot;hello&amp;quot; to a global list when compiled with LSL, and 752&lt;br /&gt;
bytes when compiled with Mono. These results are a little different than those given in&lt;br /&gt;
[[LSL_Script_Memory|this page]], and that&#039;s why it can&lt;br /&gt;
be illuminating to make these measurements in your own scripts. (In this example, the&lt;br /&gt;
[[LSL_Hacks|LSL assignment hack]] would help minimize memory consumption for the LSO case.)&lt;br /&gt;
===Caveats and geekystuffs===&lt;br /&gt;
There are several reasons why [[llGetFreeMemory]]() can give unexpected results:&lt;br /&gt;
* &#039;&#039;&#039;In Mono, memory may be allocated in blocks&#039;&#039;&#039; -- For example, a small change in the script might cause the free memory to change by 512 bytes. To get a better idea of the average memory used by a particular script construct, repeat it a number of times in the script and divide by the number of instances. For more information, see {{Jira|SVC-4387}}.&lt;br /&gt;
* &#039;&#039;&#039;[[llGetFreeMemory]]() reports the low-water mark before garbage collection&#039;&#039;&#039; -- For example, if you fill a list or string with thousands of bytes of data and then clear the list or string, [[llGetFreeMemory]]() will continue to report the lowest amount of memory that occurred and does not reflect the fact that a bunch of memory might be available if only the garbage collector would collect it. To reduce lag, the garbage collector typically frees up memory only when the script runs out of memory and more memory is needed. This is also illustrated in the code example at the end of this article. For more information, see the comments at:&lt;br /&gt;
** [[llGetFreeMemory]]()&lt;br /&gt;
** {{Jira|SVC-4387}}&lt;br /&gt;
** {{Jira|SVC-1852}}&lt;br /&gt;
* &#039;&#039;&#039;Memory fragmentation&#039;&#039;&#039; -- The order in which memory is allocated and freed can cause [[llGetFreeMemory]]() to act differently. In LSO, it might be reporting the largest single block of free memory instead of the total free bytes. It&#039;s not clear how fragmentation might affect [[llGetFreeMemory]]() in Mono. For more information, see the comments in:&lt;br /&gt;
** {{Jira|SVC-116}}&lt;br /&gt;
** {{Jira|SVC-168}}&lt;br /&gt;
* &#039;&#039;&#039;First run different than a reset&#039;&#039;&#039; -- In Mono, the amount of free memory reported by [[llGetFreeMemory]]() as the first line in [[state_entry]]() to be different on the first run after compiling compared to subsequent runs after a script reset.&lt;br /&gt;
* &#039;&#039;&#039;Mischievous spirits in Mono&#039;&#039;&#039; -- Even Mono experts describe Mono&#039;s memory management and garbage collection as squirrely and non-deterministic at best, so expect a few puzzling results in Mono.&lt;br /&gt;
&lt;br /&gt;
===Code example===&lt;br /&gt;
If you find yourself making lots of measurements and comparisons, you might find a test scaffolding useful. Here is an example of some things you can do. To run your own tests, replace the lines below in touch_start() with your own tests, using the existing code as examples. Insert a call to mem() in between each test. The results in the chat log will be easy to read, and will also automatically log if the tests were run on Mono or LSO so you won&#039;t have to remember later.&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Demo of a memory-usage reporter &lt;br /&gt;
 //&lt;br /&gt;
 // To use:  call initMemReporter() in the start of the script, then&lt;br /&gt;
 // call mem() at any time to report memory used and amount of change.&lt;br /&gt;
 // This adjusts the numbers automatically depending on whether the&lt;br /&gt;
 // script is compiled for Mono or classic LSO.&lt;br /&gt;
 //&lt;br /&gt;
 integer isMono;        // set at runtime to TRUE if Mono VM, FALSE if LSO&lt;br /&gt;
 integer totalMem;      // set at runtime to 65536 or 16384&lt;br /&gt;
 string  vm;            // set at runtime to &amp;quot;Mono&amp;quot; or &amp;quot;LSO&amp;quot;&lt;br /&gt;
 integer lastUsedMem;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 // Call this once at the start of the script, then call mem()&lt;br /&gt;
 // at any time to report memory usage. This function caches a&lt;br /&gt;
 // few variables so that they don&#039;t have to be recomputed in&lt;br /&gt;
 // mem() every time.&lt;br /&gt;
 //&lt;br /&gt;
 initMemReporter()&lt;br /&gt;
 {&lt;br /&gt;
     // Test if we&#039;re running on LSO or Mono VM using&lt;br /&gt;
     // the difference between the two reported in&lt;br /&gt;
     // https://jira.secondlife.com/browse/SVC-3760&lt;br /&gt;
 &lt;br /&gt;
     isMono = llToLower(&amp;quot;Ü&amp;quot;) != &amp;quot;Ü&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
     // Establish total memory:&lt;br /&gt;
 &lt;br /&gt;
     if (isMono) {&lt;br /&gt;
         totalMem = 65536;&lt;br /&gt;
         vm = &amp;quot;(Mono)&amp;quot;;&lt;br /&gt;
     } else {&lt;br /&gt;
         totalMem = 16384;&lt;br /&gt;
         vm = &amp;quot;(LSO)&amp;quot;;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     lastUsedMem = totalMem - llGetFreeMemory();&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Report the memory used and the change from the last report&lt;br /&gt;
 //&lt;br /&gt;
 mem(string label)&lt;br /&gt;
 {&lt;br /&gt;
     integer freeMem = llGetFreeMemory();&lt;br /&gt;
     integer usedMem = totalMem - freeMem;&lt;br /&gt;
 &lt;br /&gt;
     llOwnerSay(&lt;br /&gt;
         vm + &lt;br /&gt;
         &amp;quot; used=&amp;quot; + (string)usedMem +&lt;br /&gt;
         &amp;quot;, free=&amp;quot; + (string)freeMem +&lt;br /&gt;
         &amp;quot;, change=&amp;quot; + (string)(lastUsedMem - usedMem) +&lt;br /&gt;
         &amp;quot; \t&amp;quot; + label);&lt;br /&gt;
 &lt;br /&gt;
     lastUsedMem = usedMem;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         initMemReporter();&lt;br /&gt;
         mem(&amp;quot;initial conditions&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         integer a;&lt;br /&gt;
         float f;&lt;br /&gt;
         list intList;&lt;br /&gt;
         intList = [];&lt;br /&gt;
         integer LOOPS = 1000;&lt;br /&gt;
         integer i = LOOPS;&lt;br /&gt;
         mem(&amp;quot;Start of tests&amp;quot;);&lt;br /&gt;
         &lt;br /&gt;
         //************* Test 1&lt;br /&gt;
         intList += [0];&lt;br /&gt;
         mem(&amp;quot;after adding the first element to a list&amp;quot;);&lt;br /&gt;
         &lt;br /&gt;
         //************* Test 2&lt;br /&gt;
         while (--i &amp;gt;= 0) {&lt;br /&gt;
             intList += 0;&lt;br /&gt;
         }&lt;br /&gt;
         mem(&amp;quot;after filling a list with &amp;quot; + (string)LOOPS + &amp;quot; zeros&amp;quot;);&lt;br /&gt;
         &lt;br /&gt;
         //************* Test 3&lt;br /&gt;
         intList = [];&lt;br /&gt;
         mem(&amp;quot;after clearing the previous list&amp;quot;);&lt;br /&gt;
         &lt;br /&gt;
         //************* Test 4&lt;br /&gt;
         for (i = LOOPS; --i &amp;gt;= 0; ) {&lt;br /&gt;
             intList += 0;&lt;br /&gt;
         }&lt;br /&gt;
         mem(&amp;quot;after filling the list again with &amp;quot; + (string)LOOPS + &amp;quot; zeros&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
The results in Mono are:&lt;br /&gt;
&amp;lt;lsl&amp;gt; (Mono) used=6552, free=58984, change=-18     initial conditions&lt;br /&gt;
 (Mono) used=6668, free=58868, change=-116     Start of tests&lt;br /&gt;
 (Mono) used=6668, free=58868, change=0     after adding the first element to a list&lt;br /&gt;
 (Mono) used=25938, free=39598, change=-19270     after filling a list with 1000 zeros&lt;br /&gt;
 (Mono) used=26660, free=38876, change=-722     after clearing the previous list&lt;br /&gt;
 (Mono) used=26660, free=38876, change=0     after filling the list again with 1000 zeros &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Also see===&lt;br /&gt;
For ways to reduce memory usage in scripts, see [[User:Becky_Pippen/Script_Memory_Limits| this checklist of things you can do]].&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/CloudBeGone&amp;diff=1135137</id>
		<title>User:Becky Pippen/CloudBeGone</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/CloudBeGone&amp;diff=1135137"/>
		<updated>2011-02-28T00:14:48Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: minor wordsmithing&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Cloud-Be-Gone! A geek&#039;s reference==&lt;br /&gt;
&lt;br /&gt;
Tired of being a cloud? Already tried [[Clearing_the_cache|clearing cache and relogging]]?&lt;br /&gt;
Everyone has their own favorite&lt;br /&gt;
techniques of what to do after that, and here are my contributions.&lt;br /&gt;
Some combination of these solutions should work, but to know which solution(s)&lt;br /&gt;
to apply requires understanding the reasons why you&#039;re a cloud.&lt;br /&gt;
&lt;br /&gt;
You&#039;re a cloud because your viewer hasn&#039;t yet received your avatar appearance data&lt;br /&gt;
from the region&#039;s simulator.&lt;br /&gt;
When you log in to SL, your viewer connects to the simulator that runs that region, and&lt;br /&gt;
the simulator is responsible for getting your avatar appearance data from the central servers,&lt;br /&gt;
then it sends that data to all the viewers that need it.&lt;br /&gt;
There are two main places where your appearance data might be stuck. Either:&lt;br /&gt;
&amp;lt;ol start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;the simulator you&#039;re connected to hasn&#039;t yet received the data from the&lt;br /&gt;
[[Service_Disruptions|asset or inventory servers]], or else&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;the simulator has your appearance, but it got lost on its way to your viewer.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here is how to tell which situation applies, and what to do:&lt;br /&gt;
&lt;br /&gt;
===Stuck between asset/inventory servers and the simulator===&lt;br /&gt;
Your appearance might be stuck on the central servers for two possible reasons:&lt;br /&gt;
&amp;lt;ol start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;the [[Service_Disruptions|asset or inventory servers or other central databases]] and/or Linden Lab&#039;s&lt;br /&gt;
internal networking services are temporarily overloaded, or&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;the simulator you&#039;re connected to is overburdened.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sometimes you can check for these problems by looking at the Statistics Bar (ctrl-shift-1).&lt;br /&gt;
Expand the Simulator section and look at Sim FPS and Pending Downloads [[Improving_Region_Performance|(here&#039;s how)]]. If you see&lt;br /&gt;
Sim FPS under 45 very often, then the simulator is too busy to keep up with all its tasks in real time.&lt;br /&gt;
&#039;&#039;&#039;Solution:&#039;&#039;&#039; &#039;&#039;&#039;teleport to a different, lightly loaded region. Or relog directly into a lightly loaded region&#039;&#039;&#039;&lt;br /&gt;
[[Changing_your_login_location|(here&#039;s how to specify your login region)]].&lt;br /&gt;
&lt;br /&gt;
If Sim FPS looks ok (at 45 most of the time), and especially if there are still Pending Downloads for&lt;br /&gt;
very long, then the simulator is running ok and the problem might be more in the asset or inventory servers or Linden&#039;s networking.&lt;br /&gt;
&#039;&#039;&#039;Solution:&#039;&#039;&#039; &#039;&#039;&#039;Use any technique to change your shape, skin, hair, and eyes to items from the Library part of your Inventory&#039;&#039;&#039;&lt;br /&gt;
[[How_do_I_get_a_default_avatar|(here&#039;s how)]]. You&#039;ll be a cloud if any of those four items have not yet been received by the viewer,&lt;br /&gt;
so replace them all.&lt;br /&gt;
There is a good chance that the simulator you&#039;re connected to already&lt;br /&gt;
has copies of all the Library items and can immediately &lt;br /&gt;
send their data to your viewer without having to wait on the central servers.&lt;br /&gt;
Then you can change shape and clothes later when the&lt;br /&gt;
servers aren&#039;t so busy.&lt;br /&gt;
&lt;br /&gt;
If there are still pending downloads even in a lightly loaded region, then the asset or inventory servers&lt;br /&gt;
might just be busy temporarily. &#039;&#039;&#039;Solution:&#039;&#039;&#039; &#039;&#039;&#039;be patient, and relog again later.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===Lost on the way to the viewer===&lt;br /&gt;
If other people in the region can see your avatar but you can&#039;t, then the data may be getting lost on the way from the simulator&lt;br /&gt;
to your viewer. (Similarly, if you can see your appearance but others can&#039;t, it means your baked appearance got lost on the&lt;br /&gt;
way from your viewer to the simulator.)&lt;br /&gt;
&lt;br /&gt;
Sometimes this problem appears in the Basic section of the Statistics Bar (ctrl-shift-1) as Packet Loss&lt;br /&gt;
[[How_do_I_check_for_packet_loss_%28network_lag%29|(more about that)]]. Anything over zero is data&lt;br /&gt;
lost on the way from the simulator to your viewer. Here are some ways this could happen&lt;br /&gt;
and some solutions:&lt;br /&gt;
&amp;lt;ol start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Data arriving too fast: &#039;&#039;&#039;Solution:&#039;&#039;&#039; &#039;&#039;&#039;Lower the Network Bandwidth in Preferences&#039;&#039;&#039; [[Troubleshooting_teleporting#Turn_Down_Maximum_Bandwidth|(here&#039;s how)]].&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Sheer quantity of data: &#039;&#039;&#039;Solution:&#039;&#039;&#039; &#039;&#039;&#039;Reduce the amount of data that the simulator has to send to your viewer by reducing the Draw Distance in Preferences&#039;&#039;&#039; [[Draw_Distance|(here&#039;s how)]]&#039;&#039;&#039;, then relog.&#039;&#039;&#039;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Avatar complexity: &#039;&#039;&#039;Solution:&#039;&#039;&#039; &#039;&#039;&#039;Reduce the amount of data and work the simulator has to do to log you on by removing attachments, especially scripted attachments&#039;&#039;&#039; [[How_to_detach_an_object_from_me|(here&#039;s how)]]&#039;&#039;&#039;, then relog.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Laggy viewer: &#039;&#039;&#039;Solution:&#039;&#039;&#039; &#039;&#039;&#039;Reduce how hard your viewer has to work by adjusting the Draw Distance and graphics quality settings to minimum&#039;&#039;&#039;&lt;br /&gt;
[[Graphics_Preferences_Layout|(here&#039;s how)]]&#039;&#039;&#039;, then relog to a region where there are very few objects in view.&#039;&#039;&#039;&lt;br /&gt;
This frees up CPU cycles on your own computer so your viewer can pay more attention to the data it receives from the simulator.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Marginal network connection: This could be a problem in your network card, cables, router,&lt;br /&gt;
or your ISP. You can test for this by logging in to Second Life on a different computer or in a different location,&lt;br /&gt;
use a wired connection instead of wireless, and have your ISP check out your internet connection.&#039;&#039;&#039;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Don&#039;t give up===&lt;br /&gt;
&lt;br /&gt;
Some combination of the solutions above should work! But if it&lt;br /&gt;
doesn&#039;t, here are some final things you can do:&lt;br /&gt;
&amp;lt;ol start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Enable the [[Advanced_menu|Advanced menu]] and choose [[Debug_Settings|Debug Settings]], enter &amp;lt;code&amp;gt;RenderUnloadedAvatar&amp;lt;/code&amp;gt;, and set this to &#039;&#039;&#039;TRUE&#039;&#039;&#039;. This doesn&#039;t solve the problem, but it&lt;br /&gt;
replaces the default cloud with a default Ruth avatar while you&#039;re waiting for your real appearance to appear. And,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;You can try submitting a trouble ticket to [http://secondlife.com/support/ the Support Staff].&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Even more good resources:&lt;br /&gt;
* [[Why_do_I_look_like_a_particle_cloud|Why do I look like a particle cloud?]]&lt;br /&gt;
* [[User:Tourmaline_Falken/Character_Test_and_Clear_Cache_Instructions|Character Test and Clear Cache Instructions]]&lt;br /&gt;
&lt;br /&gt;
And for fun:&lt;br /&gt;
* [[User:Becky_Pippen/RuthCloudScript|Ruth Cloud script]]&lt;br /&gt;
&lt;br /&gt;
Good luck!&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen&amp;diff=1134217</id>
		<title>User:Becky Pippen</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen&amp;diff=1134217"/>
		<updated>2011-02-14T01:22:34Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Add links to new pages CloudBeGone and RuthCloudScript.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== About ==&lt;br /&gt;
I love helping new residents get addicted... er, I mean acclimated to Second Life. Here are some notes I&#039;ve picked up along the way that might be of help.&lt;br /&gt;
== SL Glossary ==&lt;br /&gt;
[http://www.inkwelle.com/glossary_nci.shtml Second Life&#039;s most complete glossary]&lt;br /&gt;
== What is Mono? ==&lt;br /&gt;
=== John and Mary ===&lt;br /&gt;
John and Mary are LSL scripters. John prefers the old-fashioned way of doing things, while Mary enjoys all the latest new technology, like Mono. They both write LSL scripts. John runs his scripts under the old way, and Mary runs hers using Mono.&lt;br /&gt;
&lt;br /&gt;
When John writes an LSL script and clicks the Save button, the client viewer compiles the script into proprietary bytecode and uploads it to the servers. The server runs the script by using a proprietary interpreter to interpret the bytecode. It runs slow.&lt;br /&gt;
&lt;br /&gt;
Mary writes the identical script -- no change in the LSL language syntax. When she presses &amp;quot;Save&amp;quot;, the client uploads her script text to the servers where it gets compiled into standardized CIL assembly language. (CIL is a bytecode that originally came from Microsoft&#039;s .NET technology.) Because the Linden servers are Linux machines, there&#039;s no .NET framework  available to run the CIL code. So the servers use the open-source Mono framework to execute the CIL. Mono includes an open-source JIT (&amp;quot;Just in Time&amp;quot;) compiler that converts the CIL bytecode into machine code as it&#039;s needed, and that compiled machine code is what makes the script execute faster than the old way.&lt;br /&gt;
=== How it all Flows ===&lt;br /&gt;
Old way:&lt;br /&gt;
 LSL  ---&amp;gt; LSL compiler ---&amp;gt; proprietary bytecode ---&amp;gt; LSL bytecode interpreter&lt;br /&gt;
&lt;br /&gt;
New way:&lt;br /&gt;
 LSL                        ---&amp;gt; LSL compiler   | &lt;br /&gt;
 C# (future)                ---&amp;gt; C# compiler    |  ---&amp;gt; CIL bytecode ---&amp;gt; Mono CIL execution engine&lt;br /&gt;
 Other languages (future)   ---&amp;gt; other compiler |&lt;br /&gt;
=== Do Scripts Running in Mono Get More Memory? ===&lt;br /&gt;
Yes, it&#039;s true that each Mono script gets 64K of memory to use. Non-Mono scripts have to fit inside 16K of memory. (That&#039;s compiled code plus stack plus heap.) Unfortunately, Mono-compiled code is a bit of a memory hog and can take up to 4 times as much memory as the old runtime environment. So, scripts using Mono end up with a little more memory to play with depending on the code-to-data ratio, but not four times as much.&lt;br /&gt;
=== Evolving Terminology ===&lt;br /&gt;
LSL - Used to refer to the whole system -- the language, the front-end compiler, and the back-end interpreter. Now it&#039;s confusing because it&#039;s used in one of two ways and its meaning depends on the context:  (1) LSL is the language, the source code, which has not changed with Mono. E.g., &amp;quot;Take this LSL script and compile it with Mono and it will run faster.&amp;quot; Or (2) LSL refers to the old compiler front-end and interpreter virtual machine back-end. E.g., &amp;quot;When I compile this script with LSL it runs very slowly.&amp;quot; Both meanings are used in the sentence, &amp;quot;This LSL script was compiled with LSL, but that LSL script was compiled with Mono.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The term LSO refers to the bytecode generated by the traditional LSL compiler and executed by the old LSL runtime, and is a term that can be used to distinguish the old LSL tool set from the Mono tool set.&lt;br /&gt;
=== Can We Use Other Languages? ===&lt;br /&gt;
Mono just runs CIL bytecode. It doesn&#039;t know or care what the original language was. At this time (mid-2008) only LSL source code can be compiled into CIL and run on Mono. But maybe someday we will be able to compile C# and possibly other languages into CIL which will run under Mono. A single object could contain scripts that originally were written in different languages. &lt;br /&gt;
&lt;br /&gt;
The LSL language -- the source syntax -- will probably be supported for a very long time. &lt;br /&gt;
&lt;br /&gt;
However, the servers will support both back-ends for a very long time -- the one that executes the old proprietary LSL bytecode, and the new Mono virtual machine that executes standard CIL. All the millions of existing LSL scripts compiled the old way will continue to run indefinitely. New LSL scripts may be compiled using the old LSL compiler or the new Mono compiler.&lt;br /&gt;
=== What To Expect After Mono Hits the Main Grid ===&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;Old LSL scripts already compiled:&#039;&#039;&#039;&#039;&#039; will continue to run indefinitely. You don&#039;t have to do a thing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;Any LSL script if you have the source code:&#039;&#039;&#039;&#039;&#039; can be recompiled under the old LSL compiler or the new Mono compiler, at your choice, for a long time to come.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;C# and other languages:&#039;&#039;&#039;&#039;&#039; probably added at some future time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== LSL Timings and Rates ==&lt;br /&gt;
Updated and [[User:Becky_Pippen/LSL_Performance|moved to this page]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Animation Frame Rate ==&lt;br /&gt;
=== The Problem ===&lt;br /&gt;
You may have heard rumors that animations (BVH files) get reduced to 12 FPS when uploaded or played, and there&#039;s no need to make an animation faster than 12 frames per second (FPS). Other rumors say that fast animation frame rates give smoother animations, while other rumors say that you should use the lowest animation frame rate possible. Confused? Here&#039;s some information that might help.&lt;br /&gt;
=== The SL Viewer Interpolates ===&lt;br /&gt;
Part of the confusion is that the term &amp;quot;frame rate&amp;quot; means different things in different contexts. The SL viewer renders frames of the scene, including any animations in the scene, as fast as it can. You can always see how fast the client is rendering frames by looking for the FPS number in the Basic section in the Statistics Bar (Ctrl-Shift-1). The frame rate in an animation file is just there to establish the time scale of the animation information. It&#039;s a different kind of &amp;quot;frame.&amp;quot; So how do the two &amp;quot;frame rates&amp;quot; relate and how does it all work? It&#039;s simple -- when the viewer renders a frame, it determines how far along it is in the animation and interpolates the avatar&#039;s bone rotations based on the nearest two frames in the animation file.&lt;br /&gt;
&lt;br /&gt;
Refer to the following illustration to see how you can demonstrate this interpolation. Using your favorite animation editor, make an animation file consisting of just three or four frames. The first frame is the requisite reference frame, then frame 2 is the first key frame of the animation that defines the start of a movement. Frame 3 is the last frame of the movement. Optionally you can add a frame 4 that is a copy of frame2 to make a loop. Make a large movement between frames 2 and 3, such as arms straight down in frame 2 and straight up in frame 3. Set the animation for 1 frame per second. Upload the short animation file and set the looping parameters if needed so that the animation loops continuously.&lt;br /&gt;
&lt;br /&gt;
With the animation set to one frame per second, the arms should start at the sides, then one second later be raised above the head, then two seconds after the start of the animation, the arms should be back at the sides, then repeat. What happens if the client renders frames faster than one per second? If the viewer didn&#039;t interpolate, you would expect the avatar&#039;s arms to snap suddenly from one position to the other... snap up... snap down... etc. at one-second intervals. But that&#039;s not what happens at all. Regardless of your viewer frame rate, you&#039;ll see your avatar&#039;s hands moving smoothly, up and down, with all the intermediate positions between animation frames interpolated smoothly. To examine the effect in more detail, click Advanced-&amp;gt;Character-&amp;gt;Slow Motion Animations, then you&#039;ll have a whole bunch of interpolated rendered frames to examine.&lt;br /&gt;
&lt;br /&gt;
[[Image:Animation-interpolation.jpg|500px|||Viewer interpolates animation frames]]&lt;br /&gt;
&lt;br /&gt;
=== Asynchronous Frame Rates ===&lt;br /&gt;
In the following timeline example, the top lines represent the frames in an animation file, and the bottom lines show how fast the viewer renders frames and where the rendered frames correspond in time to the animation file. In this illustration, the animation frames are numbered starting at two, because frame one is the reference frame that never gets rendered. When the client is rendering the first frame of the animation (render frame 0 in the illustration), it corresponds to frame 2 in the animation file. In this illustration, the client is running at a fairly constant rate a little more than three times the rate of frames in the animation file:&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
animation frame number        2                         3                         4                         5&lt;br /&gt;
&lt;br /&gt;
animation frames:             K-------------------------K-------------------------K-------------------------K-----&lt;br /&gt;
&lt;br /&gt;
viewer rendered frames:       |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|&lt;br /&gt;
&lt;br /&gt;
viewer frame number:          0       1       2       3       4       5       6       7       8       9      10&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
When the viewer is rendering frame 0, the bone rotations exactly correspond to frame 2 in the animation file. After that, the time when a frame is rendered will almost always fall somewhere between two of the animation frames and the client will interpolate the bone rotations. For example, when the client renders frame 1, the bone rotations will be something between the rotations in frames 2 and 3 of the animation file, but closer to the rotations in frame 2. &lt;br /&gt;
&lt;br /&gt;
If the client is running slow in comparison to the animation file, then some data in the animation file must be skipped:&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
animation frame number        2     3     4     5     6     7     8     9    10    11    12    13    14    15 &lt;br /&gt;
&lt;br /&gt;
animation key frames:         K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K---&lt;br /&gt;
&lt;br /&gt;
viewer rendered frames:       |--------------|---------------|---------------|---------------|---------------|--&lt;br /&gt;
&lt;br /&gt;
viewer frame number:          0              1               2               3               4               5 &lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
In this scenario, when it&#039;s time for the client to render frame 1, the animation information is interpolated from frames 4 and 5 in the animation file, which means that any quick movement defined in frames 3 and 4 of the animation file will have been ignored that time around. If the animation loops, then perhaps on a subsequent loop the frames will align differently and a different set of animation frames will be ignored.&lt;br /&gt;
&lt;br /&gt;
=== Other Considerations ===&lt;br /&gt;
There are additional forces at work you need to be aware of:&lt;br /&gt;
&lt;br /&gt;
1. The higher the animation frame rate, the larger the file that has to be sent from the sim to every client in viewing range of the animation. Smaller is less laggy.&lt;br /&gt;
&lt;br /&gt;
2. When you upload an animation, the viewer silently drops small movements. It does this by comparing each bone&#039;s rotation in two adjacent frames, then dropping any data for bones that don&#039;t move much. The exact algorithm is complicated, but in general the threshold is a few degrees in any axis from one frame to the next. If a bone&#039;s rotation changes from one frame to the next less than 3 or 4 degrees in all axes, it might be dropped. If the movement is more than about 10 degrees in any axis, it&#039;s likely to be retained. If an animation is made with a high frame rate, then the movement from frame to frame will be smaller and more movements will be dropped. If the animation frames are less frequent, then the movement between frames will be larger and more likely to be retained.&lt;br /&gt;
&lt;br /&gt;
3. When the viewer interpolates bone rotations from the animation file, it uses a linear interpolation between the nearest frames, which sometimes looks a bit robotic. A higher animation rate lets you specify movements in finer resolution and make the movement start slow, get faster in the middle of the movement, then slow down when it reaches its maximum extent. &lt;br /&gt;
&lt;br /&gt;
=== Summary ===&lt;br /&gt;
The last two forces mentioned above work against each other, so the challenge for animators is to find a balance. The animation needs to be made with a high enough frame rate to describe the movements the way you want them to appear, yet it needs to be as low a frame rate as possible to avoid having the client discard small movements on upload. It depends on the animation, but a good starting point might be around 10 FPS for the animation. Try a lower animation frame rate if the animation appears ok with fewer frames, relying more on the linear interpolation by the client to smooth it out. Use a higher animation frame rate if the linear interpolation doesn&#039;t look quite right and you need finer resolution to describe more complex or non-linear movements.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Script Memory Limits ==&lt;br /&gt;
Script Memory limits are coming in 2010! Here&#039;s some information to help you prepare;&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Limits_FAQ| FAQ about script memory limits]]&lt;br /&gt;
* [[User:Becky_Pippen/Measure_Script_Memory_Usage|Measuring your script memory usage]]&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Efficiency|Calculating memory needed and memory usage efficiency]]&lt;br /&gt;
* [[User:Becky_Pippen/Script_Memory_Limits| A checklist for scripters -- techniques for reducing memory usage]]&lt;br /&gt;
* [[User:Becky_Pippen/New_LSL_Functions| New LSL functions llGetLinkPrimitiveParams(), llSetLinkPrimitiveParamsFast() and more]]&lt;br /&gt;
* [[User:Becky_Pippen/Hashing| Scripting techniques for hashing data to conserve memory]]&lt;br /&gt;
* [[User:Becky_Pippen/Numeric_Storage| Scripting techniques for storing arbitrary binary data without lists]]&lt;br /&gt;
* [[User:Becky_Pippen/Text_Storage| Scripting techniques for compressing ASCII text in Mono]]&lt;br /&gt;
* [http://lslwiki.net/lslwiki/wakka.php?wakka=MemoryUsage Empirical measurements of memory usage by data type and scope (lslwiki.net)]&lt;br /&gt;
* [[LSL_Script_Memory|Empirical measurements of memory usage by data type and scope (SL wiki)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Shared Media-on-a-Prim LSL recipes ==&lt;br /&gt;
[[User:Becky_Pippen/Shared_Media_LSL_Recipes|See here]]&lt;br /&gt;
&lt;br /&gt;
==Cloud Be Gone!==&lt;br /&gt;
Tired of being a Ruth Cloud? See [[User:Becky_Pippen/CloudBeGone|this article]] for the technical reasons why&lt;br /&gt;
various solutions work or not.&lt;br /&gt;
&lt;br /&gt;
For fun, [[User:Becky_Pippen/RuthCloudScript|see here for a Ruth Cloud LSL script]].&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [http://jira.secondlife.com/ JIRA Issue Tracker]&lt;br /&gt;
* [https://wiki.secondlife.com/wiki/About_AWG Architecture Working Group]&lt;br /&gt;
{{skills&lt;br /&gt;
|Builder=*&lt;br /&gt;
|Scripter=*&lt;br /&gt;
|Terraformer=&lt;br /&gt;
|Architect=&lt;br /&gt;
|Scenographer=&lt;br /&gt;
|SLogistician=&lt;br /&gt;
|}}&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen&amp;diff=1134216</id>
		<title>User:Becky Pippen</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen&amp;diff=1134216"/>
		<updated>2011-02-14T01:18:27Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: remove a couple of obsolete links.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== About ==&lt;br /&gt;
I love helping new residents get addicted... er, I mean acclimated to Second Life. Here are some notes I&#039;ve picked up along the way that might be of help.&lt;br /&gt;
== SL Glossary ==&lt;br /&gt;
[http://www.inkwelle.com/glossary_nci.shtml Second Life&#039;s most complete glossary]&lt;br /&gt;
== What is Mono? ==&lt;br /&gt;
=== John and Mary ===&lt;br /&gt;
John and Mary are LSL scripters. John prefers the old-fashioned way of doing things, while Mary enjoys all the latest new technology, like Mono. They both write LSL scripts. John runs his scripts under the old way, and Mary runs hers using Mono.&lt;br /&gt;
&lt;br /&gt;
When John writes an LSL script and clicks the Save button, the client viewer compiles the script into proprietary bytecode and uploads it to the servers. The server runs the script by using a proprietary interpreter to interpret the bytecode. It runs slow.&lt;br /&gt;
&lt;br /&gt;
Mary writes the identical script -- no change in the LSL language syntax. When she presses &amp;quot;Save&amp;quot;, the client uploads her script text to the servers where it gets compiled into standardized CIL assembly language. (CIL is a bytecode that originally came from Microsoft&#039;s .NET technology.) Because the Linden servers are Linux machines, there&#039;s no .NET framework  available to run the CIL code. So the servers use the open-source Mono framework to execute the CIL. Mono includes an open-source JIT (&amp;quot;Just in Time&amp;quot;) compiler that converts the CIL bytecode into machine code as it&#039;s needed, and that compiled machine code is what makes the script execute faster than the old way.&lt;br /&gt;
=== How it all Flows ===&lt;br /&gt;
Old way:&lt;br /&gt;
 LSL  ---&amp;gt; LSL compiler ---&amp;gt; proprietary bytecode ---&amp;gt; LSL bytecode interpreter&lt;br /&gt;
&lt;br /&gt;
New way:&lt;br /&gt;
 LSL                        ---&amp;gt; LSL compiler   | &lt;br /&gt;
 C# (future)                ---&amp;gt; C# compiler    |  ---&amp;gt; CIL bytecode ---&amp;gt; Mono CIL execution engine&lt;br /&gt;
 Other languages (future)   ---&amp;gt; other compiler |&lt;br /&gt;
=== Do Scripts Running in Mono Get More Memory? ===&lt;br /&gt;
Yes, it&#039;s true that each Mono script gets 64K of memory to use. Non-Mono scripts have to fit inside 16K of memory. (That&#039;s compiled code plus stack plus heap.) Unfortunately, Mono-compiled code is a bit of a memory hog and can take up to 4 times as much memory as the old runtime environment. So, scripts using Mono end up with a little more memory to play with depending on the code-to-data ratio, but not four times as much.&lt;br /&gt;
=== Evolving Terminology ===&lt;br /&gt;
LSL - Used to refer to the whole system -- the language, the front-end compiler, and the back-end interpreter. Now it&#039;s confusing because it&#039;s used in one of two ways and its meaning depends on the context:  (1) LSL is the language, the source code, which has not changed with Mono. E.g., &amp;quot;Take this LSL script and compile it with Mono and it will run faster.&amp;quot; Or (2) LSL refers to the old compiler front-end and interpreter virtual machine back-end. E.g., &amp;quot;When I compile this script with LSL it runs very slowly.&amp;quot; Both meanings are used in the sentence, &amp;quot;This LSL script was compiled with LSL, but that LSL script was compiled with Mono.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The term LSO refers to the bytecode generated by the traditional LSL compiler and executed by the old LSL runtime, and is a term that can be used to distinguish the old LSL tool set from the Mono tool set.&lt;br /&gt;
=== Can We Use Other Languages? ===&lt;br /&gt;
Mono just runs CIL bytecode. It doesn&#039;t know or care what the original language was. At this time (mid-2008) only LSL source code can be compiled into CIL and run on Mono. But maybe someday we will be able to compile C# and possibly other languages into CIL which will run under Mono. A single object could contain scripts that originally were written in different languages. &lt;br /&gt;
&lt;br /&gt;
The LSL language -- the source syntax -- will probably be supported for a very long time. &lt;br /&gt;
&lt;br /&gt;
However, the servers will support both back-ends for a very long time -- the one that executes the old proprietary LSL bytecode, and the new Mono virtual machine that executes standard CIL. All the millions of existing LSL scripts compiled the old way will continue to run indefinitely. New LSL scripts may be compiled using the old LSL compiler or the new Mono compiler.&lt;br /&gt;
=== What To Expect After Mono Hits the Main Grid ===&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;Old LSL scripts already compiled:&#039;&#039;&#039;&#039;&#039; will continue to run indefinitely. You don&#039;t have to do a thing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;Any LSL script if you have the source code:&#039;&#039;&#039;&#039;&#039; can be recompiled under the old LSL compiler or the new Mono compiler, at your choice, for a long time to come.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;C# and other languages:&#039;&#039;&#039;&#039;&#039; probably added at some future time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== LSL Timings and Rates ==&lt;br /&gt;
Updated and [[User:Becky_Pippen/LSL_Performance|moved to this page]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Animation Frame Rate ==&lt;br /&gt;
=== The Problem ===&lt;br /&gt;
You may have heard rumors that animations (BVH files) get reduced to 12 FPS when uploaded or played, and there&#039;s no need to make an animation faster than 12 frames per second (FPS). Other rumors say that fast animation frame rates give smoother animations, while other rumors say that you should use the lowest animation frame rate possible. Confused? Here&#039;s some information that might help.&lt;br /&gt;
=== The SL Viewer Interpolates ===&lt;br /&gt;
Part of the confusion is that the term &amp;quot;frame rate&amp;quot; means different things in different contexts. The SL viewer renders frames of the scene, including any animations in the scene, as fast as it can. You can always see how fast the client is rendering frames by looking for the FPS number in the Basic section in the Statistics Bar (Ctrl-Shift-1). The frame rate in an animation file is just there to establish the time scale of the animation information. It&#039;s a different kind of &amp;quot;frame.&amp;quot; So how do the two &amp;quot;frame rates&amp;quot; relate and how does it all work? It&#039;s simple -- when the viewer renders a frame, it determines how far along it is in the animation and interpolates the avatar&#039;s bone rotations based on the nearest two frames in the animation file.&lt;br /&gt;
&lt;br /&gt;
Refer to the following illustration to see how you can demonstrate this interpolation. Using your favorite animation editor, make an animation file consisting of just three or four frames. The first frame is the requisite reference frame, then frame 2 is the first key frame of the animation that defines the start of a movement. Frame 3 is the last frame of the movement. Optionally you can add a frame 4 that is a copy of frame2 to make a loop. Make a large movement between frames 2 and 3, such as arms straight down in frame 2 and straight up in frame 3. Set the animation for 1 frame per second. Upload the short animation file and set the looping parameters if needed so that the animation loops continuously.&lt;br /&gt;
&lt;br /&gt;
With the animation set to one frame per second, the arms should start at the sides, then one second later be raised above the head, then two seconds after the start of the animation, the arms should be back at the sides, then repeat. What happens if the client renders frames faster than one per second? If the viewer didn&#039;t interpolate, you would expect the avatar&#039;s arms to snap suddenly from one position to the other... snap up... snap down... etc. at one-second intervals. But that&#039;s not what happens at all. Regardless of your viewer frame rate, you&#039;ll see your avatar&#039;s hands moving smoothly, up and down, with all the intermediate positions between animation frames interpolated smoothly. To examine the effect in more detail, click Advanced-&amp;gt;Character-&amp;gt;Slow Motion Animations, then you&#039;ll have a whole bunch of interpolated rendered frames to examine.&lt;br /&gt;
&lt;br /&gt;
[[Image:Animation-interpolation.jpg|500px|||Viewer interpolates animation frames]]&lt;br /&gt;
&lt;br /&gt;
=== Asynchronous Frame Rates ===&lt;br /&gt;
In the following timeline example, the top lines represent the frames in an animation file, and the bottom lines show how fast the viewer renders frames and where the rendered frames correspond in time to the animation file. In this illustration, the animation frames are numbered starting at two, because frame one is the reference frame that never gets rendered. When the client is rendering the first frame of the animation (render frame 0 in the illustration), it corresponds to frame 2 in the animation file. In this illustration, the client is running at a fairly constant rate a little more than three times the rate of frames in the animation file:&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
animation frame number        2                         3                         4                         5&lt;br /&gt;
&lt;br /&gt;
animation frames:             K-------------------------K-------------------------K-------------------------K-----&lt;br /&gt;
&lt;br /&gt;
viewer rendered frames:       |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|&lt;br /&gt;
&lt;br /&gt;
viewer frame number:          0       1       2       3       4       5       6       7       8       9      10&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
When the viewer is rendering frame 0, the bone rotations exactly correspond to frame 2 in the animation file. After that, the time when a frame is rendered will almost always fall somewhere between two of the animation frames and the client will interpolate the bone rotations. For example, when the client renders frame 1, the bone rotations will be something between the rotations in frames 2 and 3 of the animation file, but closer to the rotations in frame 2. &lt;br /&gt;
&lt;br /&gt;
If the client is running slow in comparison to the animation file, then some data in the animation file must be skipped:&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
animation frame number        2     3     4     5     6     7     8     9    10    11    12    13    14    15 &lt;br /&gt;
&lt;br /&gt;
animation key frames:         K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K---&lt;br /&gt;
&lt;br /&gt;
viewer rendered frames:       |--------------|---------------|---------------|---------------|---------------|--&lt;br /&gt;
&lt;br /&gt;
viewer frame number:          0              1               2               3               4               5 &lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
In this scenario, when it&#039;s time for the client to render frame 1, the animation information is interpolated from frames 4 and 5 in the animation file, which means that any quick movement defined in frames 3 and 4 of the animation file will have been ignored that time around. If the animation loops, then perhaps on a subsequent loop the frames will align differently and a different set of animation frames will be ignored.&lt;br /&gt;
&lt;br /&gt;
=== Other Considerations ===&lt;br /&gt;
There are additional forces at work you need to be aware of:&lt;br /&gt;
&lt;br /&gt;
1. The higher the animation frame rate, the larger the file that has to be sent from the sim to every client in viewing range of the animation. Smaller is less laggy.&lt;br /&gt;
&lt;br /&gt;
2. When you upload an animation, the viewer silently drops small movements. It does this by comparing each bone&#039;s rotation in two adjacent frames, then dropping any data for bones that don&#039;t move much. The exact algorithm is complicated, but in general the threshold is a few degrees in any axis from one frame to the next. If a bone&#039;s rotation changes from one frame to the next less than 3 or 4 degrees in all axes, it might be dropped. If the movement is more than about 10 degrees in any axis, it&#039;s likely to be retained. If an animation is made with a high frame rate, then the movement from frame to frame will be smaller and more movements will be dropped. If the animation frames are less frequent, then the movement between frames will be larger and more likely to be retained.&lt;br /&gt;
&lt;br /&gt;
3. When the viewer interpolates bone rotations from the animation file, it uses a linear interpolation between the nearest frames, which sometimes looks a bit robotic. A higher animation rate lets you specify movements in finer resolution and make the movement start slow, get faster in the middle of the movement, then slow down when it reaches its maximum extent. &lt;br /&gt;
&lt;br /&gt;
=== Summary ===&lt;br /&gt;
The last two forces mentioned above work against each other, so the challenge for animators is to find a balance. The animation needs to be made with a high enough frame rate to describe the movements the way you want them to appear, yet it needs to be as low a frame rate as possible to avoid having the client discard small movements on upload. It depends on the animation, but a good starting point might be around 10 FPS for the animation. Try a lower animation frame rate if the animation appears ok with fewer frames, relying more on the linear interpolation by the client to smooth it out. Use a higher animation frame rate if the linear interpolation doesn&#039;t look quite right and you need finer resolution to describe more complex or non-linear movements.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Script Memory Limits ==&lt;br /&gt;
Script Memory limits are coming in 2010! Here&#039;s some information to help you prepare;&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Limits_FAQ| FAQ about script memory limits]]&lt;br /&gt;
* [[User:Becky_Pippen/Measure_Script_Memory_Usage|Measuring your script memory usage]]&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Efficiency|Calculating memory needed and memory usage efficiency]]&lt;br /&gt;
* [[User:Becky_Pippen/Script_Memory_Limits| A checklist for scripters -- techniques for reducing memory usage]]&lt;br /&gt;
* [[User:Becky_Pippen/New_LSL_Functions| New LSL functions llGetLinkPrimitiveParams(), llSetLinkPrimitiveParamsFast() and more]]&lt;br /&gt;
* [[User:Becky_Pippen/Hashing| Scripting techniques for hashing data to conserve memory]]&lt;br /&gt;
* [[User:Becky_Pippen/Numeric_Storage| Scripting techniques for storing arbitrary binary data without lists]]&lt;br /&gt;
* [[User:Becky_Pippen/Text_Storage| Scripting techniques for compressing ASCII text in Mono]]&lt;br /&gt;
* [http://lslwiki.net/lslwiki/wakka.php?wakka=MemoryUsage Empirical measurements of memory usage by data type and scope (lslwiki.net)]&lt;br /&gt;
* [[LSL_Script_Memory|Empirical measurements of memory usage by data type and scope (SL wiki)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Shared Media-on-a-Prim LSL recipes ==&lt;br /&gt;
[[User:Becky_Pippen/Shared_Media_LSL_Recipes|See here]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [http://jira.secondlife.com/ JIRA Issue Tracker]&lt;br /&gt;
* [https://wiki.secondlife.com/wiki/About_AWG Architecture Working Group]&lt;br /&gt;
{{skills&lt;br /&gt;
|Builder=*&lt;br /&gt;
|Scripter=*&lt;br /&gt;
|Terraformer=&lt;br /&gt;
|Architect=&lt;br /&gt;
|Scenographer=&lt;br /&gt;
|SLogistician=&lt;br /&gt;
|}}&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/CloudBeGone&amp;diff=1134215</id>
		<title>User:Becky Pippen/CloudBeGone</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/CloudBeGone&amp;diff=1134215"/>
		<updated>2011-02-14T01:16:56Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Yet Another de-Ruthing guide&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Cloud-Be-Gone! A geek&#039;s reference==&lt;br /&gt;
&lt;br /&gt;
Tired of being a cloud? Already tried [[Clearing_the_cache|clearing cache and relogging]]?&lt;br /&gt;
Everyone has their own favorite&lt;br /&gt;
techniques of what to do after that, and here are my contributions.&lt;br /&gt;
Some combination of these solutions should work, but to know which solution(s)&lt;br /&gt;
to apply requires understanding the reasons why you&#039;re a cloud.&lt;br /&gt;
&lt;br /&gt;
You&#039;re a cloud because your viewer hasn&#039;t yet received your avatar appearance data&lt;br /&gt;
from the region&#039;s simulator.&lt;br /&gt;
When you log in to SL, your viewer connects to the simulator that runs that region, and&lt;br /&gt;
the simulator is responsible for getting your avatar appearance data from the central servers,&lt;br /&gt;
then it sends that data to all the viewers connected to the region.&lt;br /&gt;
There are two main places where your appearance data might be stuck. Either:&lt;br /&gt;
&amp;lt;ol start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;the simulator you&#039;re connected to hasn&#039;t yet received the data from the&lt;br /&gt;
[[Service_Disruptions|asset or inventory servers]], or else&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;the simulator has your appearance, but it got lost on its way to your viewer.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here is how to tell which situation applies, and what to do:&lt;br /&gt;
&lt;br /&gt;
===Stuck between asset/inventory servers and the simulator===&lt;br /&gt;
Your appearance might be stuck on the central servers for two possible reasons:&lt;br /&gt;
&amp;lt;ol start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;the [[Service_Disruptions|asset or inventory servers or other central databases]] and/or Linden Lab&#039;s&lt;br /&gt;
internal networking services are temporarily overloaded, or&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;the simulator you&#039;re connected to is overburdened.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sometimes you can check for these problems by looking at the Statistics Bar (ctrl-shift-1).&lt;br /&gt;
Expand the Simulator section and look at Sim FPS and Pending Downloads [[Improving_Region_Performance|(here&#039;s how)]]. If you see&lt;br /&gt;
Sim FPS under 45 very often, then the simulator is too busy to keep up with all its tasks in real time.&lt;br /&gt;
&#039;&#039;&#039;Solution:&#039;&#039;&#039; &#039;&#039;&#039;teleport to a different, lightly loaded region. Or relog directly into a lightly loaded region&#039;&#039;&#039;&lt;br /&gt;
[[Changing_your_login_location|(here&#039;s how to specify your login region)]].&lt;br /&gt;
&lt;br /&gt;
If Sim FPS looks ok (at 45 most of the time) but there are still Pending Downloads for&lt;br /&gt;
very long, then the simulator is running ok, and the problem might be more in the asset or inventory servers or Linden&#039;s networking.&lt;br /&gt;
&#039;&#039;&#039;Solution:&#039;&#039;&#039; &#039;&#039;&#039;Use any technique to change your shape, skin, &#039;&#039;and&#039;&#039; clothing to items from the Library part of your Inventory&#039;&#039;&#039;&lt;br /&gt;
[[How_do_I_get_a_default_avatar|(here&#039;s how)]].&lt;br /&gt;
There is a good chance that the simulator you&#039;re connected to already&lt;br /&gt;
has copies of all the Library items and can immediately &lt;br /&gt;
send their data to your viewer without having to wait on the central servers.&lt;br /&gt;
Then you can change shape and clothes later when the&lt;br /&gt;
servers aren&#039;t so busy.&lt;br /&gt;
&lt;br /&gt;
If there are still pending downloads even in a lightly loaded region, then the asset or inventory servers&lt;br /&gt;
might just be busy temporarily. &#039;&#039;&#039;Solution:&#039;&#039;&#039; &#039;&#039;&#039;be patient, and relog again later.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===Lost on the way to the viewer===&lt;br /&gt;
If other people in the region can see your avatar but you can&#039;t, then the problem is definitely between the simulator&lt;br /&gt;
and your viewer. Also you can tell if this is the problem by looking at the Statistics Bar (ctrl-shift-1).&lt;br /&gt;
Expand the Basic section and watch the Packet Loss [[How_do_I_check_for_packet_loss_%28network_lag%29|(more about that)]]. Anything over zero is data&lt;br /&gt;
lost on the way from the simulator to your viewer. Here are some ways this could happen&lt;br /&gt;
and some solutions:&lt;br /&gt;
&amp;lt;ol start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Data arriving too fast: &#039;&#039;&#039;Solution:&#039;&#039;&#039; &#039;&#039;&#039;Lower the Network Bandwidth in Preferences&#039;&#039;&#039; [[Troubleshooting_teleporting#Turn_Down_Maximum_Bandwidth|(here&#039;s how)]].&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Sheer quantity of data: &#039;&#039;&#039;Solution:&#039;&#039;&#039; &#039;&#039;&#039;Reduce the amount of data that the simulator has to send to your viewer by reducing the Draw Distance in Preferences&#039;&#039;&#039; [[Draw_Distance|(here&#039;s how)]]&#039;&#039;&#039;, then relog.&#039;&#039;&#039;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Avatar complexity: &#039;&#039;&#039;Solution:&#039;&#039;&#039; &#039;&#039;&#039;Reduce the amount of data and work the simulator has to do to log you on by removing attachments, especially scripted attachments&#039;&#039;&#039; [[How_to_detach_an_object_from_me|(here&#039;s how)]]&#039;&#039;&#039;, then relog.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Laggy viewer: &#039;&#039;&#039;Solution:&#039;&#039;&#039; &#039;&#039;&#039;Reduce how hard your viewer has to work by adjusting the Draw Distance and graphics quality settings to minimum&#039;&#039;&#039;&lt;br /&gt;
[[Graphics_Preferences_Layout|(here&#039;s how)]]&#039;&#039;&#039;, then relog to a region where there are very few objects in view.&#039;&#039;&#039;&lt;br /&gt;
This frees up CPU cycles on your own computer so your viewer can pay more attention to the data it receives from the simulator.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Marginal network connection: This could be a problem in your network card, cables, router,&lt;br /&gt;
or your ISP. You can test for this by logging in to Second Life on a different computer or in a different location,&lt;br /&gt;
use a wired connection instead of wireless, and have your ISP check out your internet connection.&#039;&#039;&#039;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Don&#039;t give up===&lt;br /&gt;
&lt;br /&gt;
Some combination of the solutions above should work! But if it&lt;br /&gt;
doesn&#039;t, here are some final things you can do:&lt;br /&gt;
&amp;lt;ol start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;Enable the [[Advanced_menu|Advanced menu]] and choose [[Debug_Settings|Debug Settings]], enter &amp;lt;code&amp;gt;RenderUnloadedAvatar&amp;lt;/code&amp;gt;, and set this to &#039;&#039;&#039;TRUE&#039;&#039;&#039;. This doesn&#039;t solve the problem, but it&lt;br /&gt;
replaces the default cloud with a default Ruth avatar while you&#039;re waiting for your real appearance to appear. And,&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;You can try submitting a trouble ticket to [http://secondlife.com/support/ the Support Staff].&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Even more good resources:&lt;br /&gt;
* [[Why_do_I_look_like_a_particle_cloud|Why do I look like a particle cloud?]]&lt;br /&gt;
* [[User:Tourmaline_Falken/Character_Test_and_Clear_Cache_Instructions|Character Test and Clear Cache Instructions]]&lt;br /&gt;
&lt;br /&gt;
And for fun:&lt;br /&gt;
* [[User:Becky_Pippen/RuthCloudScript|Ruth Cloud script]]&lt;br /&gt;
&lt;br /&gt;
Good luck!&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/RuthCloudScript&amp;diff=1134214</id>
		<title>User:Becky Pippen/RuthCloudScript</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/RuthCloudScript&amp;diff=1134214"/>
		<updated>2011-02-14T01:09:37Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Yet Another version of a Ruth Cloud LSL script.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==The Ruth Cloud==&lt;br /&gt;
&lt;br /&gt;
Just for giggles, here are the secret parameter settings used to make a Ruth Cloud.&lt;br /&gt;
There was already a copy of a&lt;br /&gt;
[[Ruth#Cloud_texture_and_particle_parameters|Ruth Particle Cloud script on the wiki]],&lt;br /&gt;
but this version corrects one minor error&lt;br /&gt;
in one parameter, and adds a scaling factor that you can change to make the cloud larger or smaller than normal.&lt;br /&gt;
Just copy and paste this into a script and drop it into any prim to make a Ruth Cloud. Touch to turn&lt;br /&gt;
on and off. See the comments in the script for more information.&lt;br /&gt;
&lt;br /&gt;
Are you tired of being a Ruth Cloud? See [[User:Becky_Pippen/CloudBeGone|this article]] for some suggestions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // This is the default Ruth cloud. The particle parameters were extracted from the viewer source file&lt;br /&gt;
 // indra/slbrowser/newview/llvoavatar.cpp, in function void LLVOAvatar::idleUpdateLoadingEffect(),&lt;br /&gt;
 // (near line 2856 in version 1.23.4). The original viewer source code comment is:&lt;br /&gt;
 //&lt;br /&gt;
 //           // fancy particle cloud designed by Brent&lt;br /&gt;
 //&lt;br /&gt;
 // referring to Brent Linden. Thanks Brent, they&#039;re cute! Now... just make them go away, ok? :-)&lt;br /&gt;
 &lt;br /&gt;
 // This script was concocted, formulated, and priminized by ❤ Becky Pippen ❤&lt;br /&gt;
 &lt;br /&gt;
 // Set the scale of the Ruth cloud here. A scale of 1.0 makes a normal sized&lt;br /&gt;
 // cloud; 0.5 makes one half as high and wide as normal; 2.0 makes one twice as&lt;br /&gt;
 // wide and high as normal. The maximum scale is 4.0, because that makes the&lt;br /&gt;
 // particles the maximum size allowed by the SL particle system.&lt;br /&gt;
 //&lt;br /&gt;
 float Scale = 4.0;  // must be in the range (0.0..4.0]&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 integer ItIsCloudy;&lt;br /&gt;
 list RuthCloudUnscaledParameters;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     on_rez(integer param)&lt;br /&gt;
     {&lt;br /&gt;
         if (Scale &amp;lt;= 0.0 || Scale &amp;gt; 4.0) {&lt;br /&gt;
             llSay(0, &amp;quot;Oops! Please edit this script and change the variable named\n&amp;quot; +&lt;br /&gt;
                      &amp;quot;Scale to a number above 0.0, and no greater than 4.0.&amp;quot;);&lt;br /&gt;
         } else {&lt;br /&gt;
             if (llGetPos() == llGetLocalPos()) {&lt;br /&gt;
                 // We&#039;re in a single prim, or root prim, and not in an attachment,&lt;br /&gt;
                 // so adjust our height a little based on the scale of the Ruth Cloud&lt;br /&gt;
                 // so that the cloud won&#039;t be half underground: (Comment out this&lt;br /&gt;
                 // line if you don&#039;t want that):&lt;br /&gt;
 &lt;br /&gt;
                 llSetPos(llGetPos() + &amp;lt;0.0, 0.0, Scale / 2.0&amp;gt;);&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer flags = PSYS_PART_INTERP_COLOR_MASK | &lt;br /&gt;
                         PSYS_PART_INTERP_SCALE_MASK |&lt;br /&gt;
                         PSYS_PART_EMISSIVE_MASK |&lt;br /&gt;
                         PSYS_PART_FOLLOW_SRC_MASK |&lt;br /&gt;
                         PSYS_PART_TARGET_POS_MASK;&lt;br /&gt;
 &lt;br /&gt;
         RuthCloudUnscaledParameters = [&lt;br /&gt;
             PSYS_PART_FLAGS, flags,&lt;br /&gt;
             PSYS_PART_MAX_AGE, 4.0,                  // particle age (yes, 4, not 3)&lt;br /&gt;
             PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_ANGLE_CONE,   // major mode&lt;br /&gt;
             PSYS_PART_START_COLOR, &amp;lt;1.0, 1.0, 1.0&amp;gt;,  // starting particle color&lt;br /&gt;
             PSYS_PART_END_COLOR, &amp;lt;1.0, 1.0, 1.0&amp;gt;,    // final color (&amp;lt;1,1,1&amp;gt;=white)&lt;br /&gt;
             PSYS_PART_START_ALPHA, 0.5,              // beginning alpha (1.0=opaque)&lt;br /&gt;
             PSYS_PART_END_ALPHA, 0.0,                // final alpha (0.0=clear)&lt;br /&gt;
             PSYS_SRC_TEXTURE, &amp;quot;f525c30a-1bde-c858-a907-3d4df5e736a7&amp;quot;, // cloud-particle.j2c&lt;br /&gt;
             PSYS_SRC_BURST_RATE, 0.02,               // burst interval&lt;br /&gt;
             PSYS_SRC_BURST_PART_COUNT, 1,            // particle count per burst&lt;br /&gt;
             PSYS_SRC_BURST_RADIUS, 0.0,              // burst radius&lt;br /&gt;
             PSYS_SRC_MAX_AGE, 0.0,                   // overall duration&lt;br /&gt;
             PSYS_SRC_ANGLE_BEGIN, 3.14159,           // starting angle 180°&lt;br /&gt;
             PSYS_SRC_ANGLE_END, 0.0                  // ending angle&lt;br /&gt;
         ];&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         ItIsCloudy = !ItIsCloudy;&lt;br /&gt;
 &lt;br /&gt;
         if (ItIsCloudy) {&lt;br /&gt;
             llParticleSystem(RuthCloudUnscaledParameters + [&lt;br /&gt;
                     PSYS_SRC_BURST_SPEED_MAX, Scale,&lt;br /&gt;
                     PSYS_SRC_BURST_SPEED_MIN, Scale * 0.1,&lt;br /&gt;
                     PSYS_PART_START_SCALE,    Scale * &amp;lt;0.8, 1.0, 0.0&amp;gt;,&lt;br /&gt;
                     PSYS_PART_END_SCALE,      Scale * &amp;lt;0.02, 0.02, 0.0&amp;gt; ]);&lt;br /&gt;
         } else {&lt;br /&gt;
             llParticleSystem([]);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 }&amp;lt;/lsl&amp;gt;&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Shared_Media_LSL_Recipes&amp;diff=890812</id>
		<title>User:Becky Pippen/Shared Media LSL Recipes</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Shared_Media_LSL_Recipes&amp;diff=890812"/>
		<updated>2010-05-03T18:48:24Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: tweaks&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==How-to&#039;s with Media-on-a-Prim==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
  MediaWiki issue: If anyone knows how to highlight parts of code inside the &amp;lt;lsl&amp;gt;&lt;br /&gt;
  wrapper, let me know. I resorted to using a style attribute in a &amp;lt;span&amp;gt; element&lt;br /&gt;
  inside a &amp;lt;tt&amp;gt; element to yellow-highlight preformatted monospace text. It would&lt;br /&gt;
  be nicer if we could also get the LSL syntax highlighting, but if we have to choose&lt;br /&gt;
  one or the other, I think the yellow highlighting is more useful and instructive&lt;br /&gt;
  for this particular page than LSL syntax highlighting.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The examples here assume you&#039;re comfortable with LSL, and you just need a quick reference for the syntax of handling HTML and JavaScript in LSL for Shared Media scripting. All the examples on this page are ready to copy-n-paste and drop into a prim — no other setup needed. The key syntax in each example is highlighted. There&#039;s no error handling in these minimalist examples, so add plenty of your own.&lt;br /&gt;
&lt;br /&gt;
===Display plain text — XyText replacement===&lt;br /&gt;
[[Image:MoaP-example_1.png‎|thumb|Plain text]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;integer face = 4;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string message = &amp;quot;Hello World&amp;quot;;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llSetPrimMediaParams(face,&amp;lt;/span&amp;gt;&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;[PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message]);&amp;lt;/span&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* An avatar must press the Reload button on the browser to see this rendered in-world. A page will be auto-loaded if you include the parameter &amp;lt;tt&amp;gt;PRIM_MEDIA_AUTO_PLAY, TRUE&amp;lt;/tt&amp;gt;, and if the avatar has auto-load enabled in preferences.&lt;br /&gt;
* There&#039;s no need to add any %-hex-escaping or to use [[llEscapeURL]](); llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, url]) will automatically escape the string for you.&lt;br /&gt;
* This works by feeding a data URI to the on-prim browser. Think of it as serving the contents of the page in the URL itself. For more information, see [[Shared_Media_and_data_URI|here]] and [http://en.wikipedia.org/wiki/Data_URI_scheme here].&lt;br /&gt;
* The in-world browser limits the data URI (or any address) to 1024 bytes after %-hex-escaping and encoded in UTF-8. See below for tricks using external or self-served JavaScript or CSS to leverage the data URI.&lt;br /&gt;
* If the data URI contains just plain text, the prefix &amp;lt;tt&amp;gt;&amp;quot;data:text/html,&amp;quot;&amp;lt;/tt&amp;gt; can be abbreviated &amp;lt;tt&amp;gt;&amp;quot;data:,&amp;quot;&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Force a page reload===&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
[[Image:MoaP-example-refreshpage.gif|thumb|Auto-refresh]]&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string myURL;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;integer seq = 0; // sequence number for unique URLs&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             myURL = body;&lt;br /&gt;
             llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_AUTO_PLAY, TRUE,&lt;br /&gt;
                  PRIM_MEDIA_CURRENT_URL, myURL]);&lt;br /&gt;
             llSetTimerEvent(5.0);&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             llHTTPResponse(id, 200, &amp;quot;Sim FPS: &amp;quot; + (string)llGetRegionFPS());&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     timer()&lt;br /&gt;
     {&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_CURRENT_URL, myURL + &amp;quot;/?r=&amp;quot; + (string)(++seq)&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
Comments:&lt;br /&gt;
* The page will reload automatically if &amp;lt;tt&amp;gt;PRIM_MEDIA_AUTO_PLAY&amp;lt;/tt&amp;gt; is enabled and if the new &amp;lt;tt&amp;gt;PRIM_MEDIA_CURRENT_URL&amp;lt;/tt&amp;gt; is different. This technique appends a short dummy parameter (like &amp;lt;tt&amp;gt;&amp;quot;/?r=12&amp;quot;&amp;lt;/tt&amp;gt;) to the end of the URL to make it different each time, forcing a reload. The parameter is otherwise ignored.&lt;br /&gt;
&lt;br /&gt;
===Display text with HTML markup===&lt;br /&gt;
[[Image:MoaP-example-markup.png‎‎|thumb|HTML markup]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string message = &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;quot;&amp;amp;lt;i&amp;gt;Hello&amp;amp;lt;/i&amp;gt;&amp;amp;lt;h2&amp;gt;World!&amp;amp;lt;/h2&amp;gt;&amp;quot;&amp;lt;/span&amp;gt;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* The text of the data URI will be %-hex-escaped automatically for you, so there&#039;s no need to use [[llEscapeURL]](). All you need in the data URI is the prefix &amp;lt;tt&amp;gt;data:text/html,&amp;lt;/tt&amp;gt; plus your HTML.&lt;br /&gt;
&lt;br /&gt;
===Change window resolution===&lt;br /&gt;
[[Image:MoaP-example-size.png‎|thumb|Resized web window]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string message = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message,&lt;br /&gt;
                 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_WIDTH_PIXELS, 128&amp;lt;/span&amp;gt;,&lt;br /&gt;
                 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_HEIGHT_PIXELS, 32&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* The browser will add scroll bars if the rendered HTML doesn&#039;t fit within the specified PRIM_MEDIA_* size.&lt;br /&gt;
&lt;br /&gt;
===Display an image — works with animated GIFs too===&lt;br /&gt;
[[Image:MoaP-example-animgif.gif‎|thumb|Image on a prim]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL =&lt;br /&gt;
           &amp;quot;http://upload.wikimedia.org/wikipedia/commons/7/70/Rotating_earth_(small).gif&amp;quot;;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string dataURI = &amp;quot;data:text/html,&amp;amp;lt;object data=&#039;&amp;quot; + imageURL + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/object&amp;gt;&amp;quot;;&amp;lt;/span&amp;gt;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [PRIM_MEDIA_CURRENT_URL, dataURI,&lt;br /&gt;
              PRIM_MEDIA_WIDTH_PIXELS, 256,&lt;br /&gt;
              PRIM_MEDIA_HEIGHT_PIXELS, 256]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* If attempting to display YouTube video full-frame, see [https://blogs.secondlife.com/community/community/tnt/blog/2010/03/16/viewer-2-tip-shared-media-show-youtube-on-a-full-prims-face this discussion].&lt;br /&gt;
* For images, you can also use &amp;lt;tt&amp;gt;&amp;amp;lt;image src= &amp;gt;&amp;lt;/tt&amp;gt; like this:&lt;br /&gt;
 string dataURI=&amp;quot;data:text/html,&amp;lt;img src=&#039;&amp;quot; + imageURL + &amp;quot;&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
===Display a background image, tiled===&lt;br /&gt;
&lt;br /&gt;
====Method #1 — using CSS in a style element in &amp;lt;head&amp;gt;====&lt;br /&gt;
[[Image:MoaP-example-bgTiled.png‎|thumb|Tiled background image]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL =&lt;br /&gt;
             &amp;quot;http://www.google.com/intl/en_ALL/images/logo.gif&amp;quot;;&lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;amp;lt;head&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;style type=&#039;text/css&#039;&amp;gt;body{background-image:url(\&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;imageURL + &amp;quot;\&amp;quot;);}&amp;amp;lt;/style&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;amp;lt;body&amp;gt;Hello World&amp;amp;lt;/body&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Method #2  — using background attribute in &amp;amp;lt;body&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL = &amp;quot;http://www.google.com/intl/en_ALL/images/logo.gif&amp;quot;;&lt;br /&gt;
         string dataURI = &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;data:text/html,&amp;amp;lt;body background=&#039;&amp;quot; + imageURL + &amp;quot;&#039;&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;Hello World&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Method #3 — using style attribute in &amp;lt;body&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL = &amp;quot;http://www.google.com/intl/en_ALL/images/logo.gif&amp;quot;;&lt;br /&gt;
         string dataURI = &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;data:text/html,&amp;amp;lt;body style=&#039;background-image:url(\&amp;quot;&amp;quot; + imageURL + &amp;quot;\&amp;quot;)&#039;&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;Hello World&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Text colors — using &amp;amp;lt;font&amp;gt; element===&lt;br /&gt;
[[Image:MoaP-example-font-color.png‎|thumb|Font color]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string message = &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;font color=&#039;Red&#039;&amp;gt;Hello World&amp;amp;lt;/font&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 }  &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* You can also specify color and other CSS rules in a style attribute in another element. For example, you can replace message in the example above with:&lt;br /&gt;
&lt;br /&gt;
 string message = &amp;quot;&amp;amp;lt;h2 style=&#039;color:#FF0000&#039;&amp;gt;Hello World&amp;amp;lt;/h2&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
===Link to external CSS file (forms example)===&lt;br /&gt;
[[Image:MoaP-example-externalCSS.png‎|thumb|External CSS file]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string externalCSS =&amp;lt;/span&amp;gt;&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;quot;http://www.google.com/css/modules/buttons/g-button-chocobo.css&amp;quot;;&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;amp;lt;head&amp;gt;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;link href=&#039;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
                 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;externalCSS + &amp;quot;&#039; rel=&#039;stylesheet&#039; type=&#039;text/css&#039; /&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;quot; +&lt;br /&gt;
                 &amp;quot;&amp;amp;lt;form&amp;gt;&amp;amp;lt;input type=&#039;button&#039; value=&#039;Click Me&#039; class=&#039;g-button&#039; /&amp;gt;&amp;amp;lt;/form&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 }  &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* This is one way to work around the 1024-byte limitation of data URIs. The external CSS can be as large as the browser permits. Also see the examples below for putting JavaScript in an external file.&lt;br /&gt;
* The tag &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;lt;/tt&amp;gt; and the attribute &amp;lt;tt&amp;gt;type=&#039;text/css&#039;&amp;lt;/tt&amp;gt; may be omitted to make the data URI a few bytes shorter.&lt;br /&gt;
&lt;br /&gt;
===Calling external JavaScript functions (JQuery example)===&lt;br /&gt;
[[Image:MoaP-example-JQuery.gif‎|thumb|Calling external JQuery]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string externalJavascript =&lt;br /&gt;
     &amp;quot;http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js&amp;quot;;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;amp;lt;head&amp;gt;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;script src=&#039;&amp;quot; + externalJavascript + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;amp;lt;button&amp;gt;Toggle&amp;amp;lt;/button&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;amp;lt;p&amp;gt;Hello&amp;amp;lt;br /&amp;gt;World&amp;amp;lt;/p&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;script&amp;gt;$(&#039;button&#039;).click(function () &amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;{$(&#039;p&#039;).slideToggle(&#039;slow&#039;);});&amp;amp;lt;/script&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* This is a technique for working around the 1024-byte limit for data URIs. When you can&#039;t fit all the JavaScript you need into one data URI, just move some JavaScript functions into an external file and give the browser a data URI that references the external file with a URL. In this example above, the JavaScript for [http://jquery.com/ JQuery] lives in an external file on Google&#039;s servers. You just need enough bytes left over in the data URI to put a JavaScript function call with its parameters inside a &amp;lt;tt&amp;gt;&amp;amp;lt;script&amp;gt;&amp;lt;/tt&amp;gt; element.&lt;br /&gt;
* A data URI can be used as a springboard to generate arbitrary complex web pages by making use of these elements:&lt;br /&gt;
** a reference to external CSS with &amp;lt;tt&amp;gt;&amp;amp;lt;link href= &amp;lt;/tt&amp;gt;, &lt;br /&gt;
** a reference to external JavaScript with &amp;lt;tt&amp;gt;&amp;amp;lt;script src=&amp;lt;/tt&amp;gt;, and&lt;br /&gt;
** a function call with parameters to the external JavaScript using &amp;lt;tt&amp;gt;&amp;amp;lt;script&amp;gt;someFunction(args);&amp;amp;lt;/script&amp;gt;&amp;lt;/tt&amp;gt;. (In the JQuery example above, the name of the external function is &amp;lt;tt&amp;gt;&amp;quot;$&amp;quot;&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* See the next two examples for variations of this same technique.&lt;br /&gt;
&lt;br /&gt;
===Self-served HTML and JavaScript===&lt;br /&gt;
====Example #1 — using &amp;amp;lt;script src= and .innerHTML= ====&lt;br /&gt;
[[Image:MoaP-example-refresh-clock.gif‎|thumb|Self-served auto-refreshed pages]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string myURL;&lt;br /&gt;
 &lt;br /&gt;
 // This can be up to 2KBytes after %-hex-escaping:&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string servedPage =&amp;lt;/span&amp;gt; &amp;quot;&lt;br /&gt;
 function checklength(i){if (i&amp;lt;10) {i=&#039;0&#039;+i;} return i;}      &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;function clock()&amp;lt;/span&amp;gt;{ &lt;br /&gt;
  var now = new Date();  &lt;br /&gt;
  var hours = checklength(now.getHours());  &lt;br /&gt;
  var minutes = checklength(now.getMinutes());  &lt;br /&gt;
  var seconds = checklength(now.getSeconds());  &lt;br /&gt;
  var format = 1; //0=24 hour format, 1=12 hour format   &lt;br /&gt;
  var time;  &lt;br /&gt;
  if (format == 1) { &lt;br /&gt;
   if (hours &amp;gt;= 12) {  &lt;br /&gt;
    if (hours ==12 ) { hours = 12;&lt;br /&gt;
    } else { hours = hours-12; } &lt;br /&gt;
    time=hours+&#039;:&#039;+minutes+&#039;:&#039;+seconds+&#039; PM&#039;;   &lt;br /&gt;
   } else if(hours &amp;lt; 12) { &lt;br /&gt;
    if (hours ==0) {hours=12;}   &lt;br /&gt;
    time=hours+&#039;:&#039;+minutes+&#039;:&#039;+seconds+&#039; AM&#039;;   &lt;br /&gt;
   }   &lt;br /&gt;
  }  &lt;br /&gt;
  if (format == 0) {time= hours+&#039;:&#039;+minutes+&#039;:&#039;+seconds;}   &lt;br /&gt;
  &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;document.getElementById(&#039;clock&#039;).innerHTML=time;&amp;lt;/span&amp;gt;&lt;br /&gt;
  setTimeout(&#039;clock();&#039;, 500); &lt;br /&gt;
 } &amp;quot;; // yes, that&#039;s one long string.&lt;br /&gt;
 &lt;br /&gt;
 displayPage()&lt;br /&gt;
 {&lt;br /&gt;
     string dataURI = &amp;quot;data:text/html,&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;script src=&#039;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;myURL + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;div id=&#039;clock&#039;&amp;gt;&amp;amp;lt;script&amp;gt;clock();&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/div&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
     llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;myURL = body;&amp;lt;/span&amp;gt;&lt;br /&gt;
             displayPage();&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPResponse(id, 200, servedPage);&amp;lt;/span&amp;gt;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* This is a technique for trading the 1024-byte limit of data URIs for the 2048-byte-per-page limit that the script can serve itself. As in the previous example, when you can&#039;t fit all the JavaScript into one data URI, just move the JavaScript into an external file and reference the file with &amp;lt;tt&amp;gt;&amp;amp;lt;script src=URL&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;lt;/tt&amp;gt;. When the URL refers to the script&#039;s own HTTP-in URL, then the page served can contain up to 2K bytes, so you get at least that many bytes plus whatever logic you can fit into the rest of the data URI. If you move that JavaScript to an external web server, the size of the external JavaScript file can be as large as the client web browser can render.&lt;br /&gt;
* Your script can serve an arbitrary number of different pages through one HTTP-in URL by appending a path or parameter to the URL.&lt;br /&gt;
* This works by assigning a string containing HTML to the &amp;lt;tt&amp;gt;.innerHTML&amp;lt;/tt&amp;gt; contents of the &amp;lt;tt&amp;gt;&amp;amp;lt;div&amp;gt;&amp;lt;/tt&amp;gt; element with id &amp;quot;clock&amp;quot;. This achieves something similar to the Ajax-like [http://talirosca.wikidot.com/bootstrapping-html-on-a-prim technique published by Tali Rosca] and mentioned [http://wiki.secondlife.com/wiki/User:Kelly_Linden/lsl_hacks#DHTML_.2F_Javascript_.28Tali_Rosca.29 here]. In those techniques, a string is constructed containing an &amp;lt;tt&amp;gt;&amp;amp;lt;a href=&amp;lt;/tt&amp;gt; tag and an &amp;lt;tt&amp;gt;href=&amp;lt;/tt&amp;gt; that points to the script&#039;s HTTP-in URL. That string then replaces the &amp;lt;tt&amp;gt;&amp;amp;lt;body&amp;gt;&amp;lt;/tt&amp;gt; element using:&lt;br /&gt;
 &amp;lt;tt&amp;gt;document.getElementsByTagName(&#039;body&#039;)[0].innerHTML = &amp;lt;i&amp;gt;new-content&amp;lt;/i&amp;gt;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Example #2 — using &amp;lt;body onload= (lag graph example)====&lt;br /&gt;
[[Image:MoaP-example-laggraph.gif‎|thumb|Refreshed pages]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string jsURL; // where to fetch external JavaScript&lt;br /&gt;
 list numbers;&lt;br /&gt;
 integer numSamples = 50;&lt;br /&gt;
 &lt;br /&gt;
 // This is self-served in this example, but can be&lt;br /&gt;
 // moved to an external server:&lt;br /&gt;
 //&lt;br /&gt;
 string externalJavascript()&lt;br /&gt;
 {&lt;br /&gt;
     return&lt;br /&gt;
         &amp;quot;function bar(widthPct,heightPix) {&amp;quot; +&lt;br /&gt;
         &amp;quot; document.writeln(\&amp;quot;&amp;amp;lt;hr style=&#039;padding:0;margin:0;&amp;quot; +&lt;br /&gt;
         &amp;quot;  margin-top:-1px;text-align:left;align:left;border=0;&amp;quot; +&lt;br /&gt;
         &amp;quot;  width:\&amp;quot;+widthPct+\&amp;quot;%;height:\&amp;quot;+heightPix+\&amp;quot;px;&amp;quot; +&lt;br /&gt;
         &amp;quot;  background-color:#c22;color:#c22;&#039;&amp;gt;\&amp;quot;);}&amp;quot; +&lt;br /&gt;
         &amp;quot; function graphBars(arr){for(var i=0;i&amp;amp;lt;arr.length;++i)&amp;quot; +&lt;br /&gt;
         &amp;quot;  {bar(arr[i],18);}}&amp;quot;;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llClearPrimMedia(face);&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
         integer i = numSamples;&lt;br /&gt;
         while (--i &amp;gt;= 0) {&lt;br /&gt;
             numbers += 0;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     timer()&lt;br /&gt;
     {&lt;br /&gt;
         numbers = llList2List(numbers, 1, -1) + [(integer)(6.0 * (45.0 - llGetRegionFPS()))];&lt;br /&gt;
 &lt;br /&gt;
         // The dataURI loads external JavaScript functions and calls one with parameters:&lt;br /&gt;
 &lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;head&amp;gt;&amp;amp;lt;script src=&#039;&amp;quot; + jsURL + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;body onload=\&amp;quot;graphBars([&amp;quot; + llList2CSV(numbers) + &amp;quot;]);\&amp;quot;&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, dataURI, PRIM_MEDIA_AUTO_PLAY, TRUE]);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             jsURL = body; // self-serve the JavaScript&lt;br /&gt;
             llSetTimerEvent(1.0);&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             llHTTPResponse(id, 200, externalJavascript());&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* Like the previous example, this is another variation of how to structure a data URI to serve as a springboard for arbitrarily large pages. In this example, the data URI first uses a &amp;lt;tt&amp;gt;&amp;amp;lt;script src=...&amp;gt;&amp;lt;/tt&amp;gt; tag to read a file of external JavaScript functions in the &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;lt;/tt&amp;gt; element, and then the &amp;lt;tt&amp;gt;onload=&amp;lt;/tt&amp;gt; attribute in &amp;lt;tt&amp;gt;&amp;amp;lt;body&amp;gt;&amp;lt;/tt&amp;gt; triggers a call to one of the functions which executes a loop creating a bunch of &amp;lt;tt&amp;gt;&amp;amp;lt;hr&amp;gt;&amp;lt;/tt&amp;gt; elements that form the bar chart. The size of HTML generated by the JavaScript can be as large as the browser permits.&lt;br /&gt;
* In this example, &amp;lt;tt&amp;gt;jsURL&amp;lt;/tt&amp;gt; is set to the script&#039;s own HTTP-in URL so that the example can be self-contained, but you can point &amp;lt;tt&amp;gt;jsURL&amp;lt;/tt&amp;gt; to an external URL as well. If served by an external server, the JavaScript read in &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;lt;/tt&amp;gt; can be as large as the browser permits.&lt;br /&gt;
* &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;lt;/tt&amp;gt; can be omitted in the data URI surrounding the &amp;lt;tt&amp;gt;&amp;amp;lt;script src=&amp;lt;/tt&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
===Reverse Ajax - Long-polling the HTTP-in server, chat logger example===&lt;br /&gt;
[[Image:MoaP-example-Long-polling-httpin.gif|thumb|300px|Long-polling HTTP-in]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 // Reverse Ajax: Long-polling HTTP-in.&lt;br /&gt;
 // Becky Pippen, 2010, contributed to the public domain.&lt;br /&gt;
 &lt;br /&gt;
 integer face = 4;          // Prim face for Shared Media&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string  myURL;             // HTTP-in URL&amp;lt;/span&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;key inId = NULL_KEY;       // GET request id&amp;lt;/span&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;list msgQueue = [];        // strings of Javascript&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;// url is our own HTTP-in url.&lt;br /&gt;
 // This sets up a bootloader web page like this:&lt;br /&gt;
 //      &amp;amp;lt;html&amp;gt;&amp;amp;lt;body&amp;gt;&lt;br /&gt;
 //         &amp;amp;lt;div&amp;gt;&amp;amp;lt;script id=&#039;sc&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/div&amp;gt;&lt;br /&gt;
 //         &amp;amp;lt;script&amp;gt; callbacks and poll.beg() defined here &amp;amp;lt;/script&amp;gt;&lt;br /&gt;
 //         &amp;amp;lt;button onclick=poll.beg()&amp;gt;Start&amp;amp;lt;/button&amp;gt;&lt;br /&gt;
 //         &amp;amp;lt;div id=&#039;dv&#039;&amp;gt;&amp;amp;lt;/div&amp;gt;&lt;br /&gt;
 //      &amp;amp;lt;/body&amp;gt;&amp;amp;lt;/html&amp;gt;&lt;br /&gt;
 // When the button is pressed, the JS code sets src= on script#sc&lt;br /&gt;
 // and reattaches the script element to the parent &amp;amp;lt;div&amp;gt; element which&lt;br /&gt;
 // initiates a GET to the prim&#039;s HTTP-in port&lt;br /&gt;
 //&lt;br /&gt;
 setDataURI(string url)&lt;br /&gt;
 {&lt;br /&gt;
     string dataURI = &amp;quot;data:text/html,&lt;br /&gt;
 &amp;amp;lt;!DOCTYPE HTML&amp;gt;&amp;amp;lt;html&amp;gt;&amp;amp;lt;body&amp;gt;&amp;amp;lt;div&amp;gt;&amp;amp;lt;script id=&#039;sc&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/div&amp;gt;&amp;amp;lt;script&amp;gt;&lt;br /&gt;
 var poll=function(){var sc=document.getElementById(&#039;sc&#039;),t2,seq=0,s0;return{&lt;br /&gt;
 beg:function(){s0=document.createElement(&#039;script&#039;);s0.onload=poll.end;t2=setTimeout(&#039;poll.end()&#039;,20000);&lt;br /&gt;
 s0.src=&#039;&amp;quot; + url + &amp;quot;/?r=&#039;+(seq++);sc.parentNode.replaceChild(s0,sc);sc=s0;},&lt;br /&gt;
 end:function(){clearTimeout(t2);t2=null;sc.onload=null;setTimeout(&#039;poll.beg()&#039;,500);},};}();&amp;amp;lt;/script&amp;gt;&lt;br /&gt;
 &amp;amp;lt;button id=&#039;btn&#039;onclick=poll.beg()&amp;gt;Start&amp;amp;lt;/button&amp;gt;&amp;amp;lt;div id=&#039;dv&#039;&amp;gt;&amp;amp;lt;/div&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
     llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, dataURI]);&amp;lt;/span&amp;gt;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;// Returns zero or more queued messages. Assumes no single message is&lt;br /&gt;
 // longer than MAX_SIZE_CHARS (will hang if there is)&lt;br /&gt;
 //&lt;br /&gt;
 string popQueuedMessages()&lt;br /&gt;
 {&lt;br /&gt;
     string  totalMsg = &amp;quot;&amp;quot;;&lt;br /&gt;
     integer totalMsgSize = 0;&lt;br /&gt;
     string  nextMsg = &amp;quot;&amp;quot;;&lt;br /&gt;
     integer nextMsgSize = 0;&lt;br /&gt;
 &lt;br /&gt;
     // HTTP response bodies are limited to 2048 bytes after encoding&lt;br /&gt;
     // in UTF-8. LSL string sizes are measured in characters, which,&lt;br /&gt;
     // in UTF-8, use one byte (for ASCII chars), two bytes (most Latin-1),&lt;br /&gt;
     // or three bytes (a few international characters). So, unless&lt;br /&gt;
     // you re-write this section so that it measures UTF-8 size, keep&lt;br /&gt;
     // MAX_SIZE_CHARS small enough so the text will fit in a response body.&lt;br /&gt;
     //&lt;br /&gt;
     integer MAX_SIZE_CHARS = 1000; // Max HTTP body size &lt;br /&gt;
  &lt;br /&gt;
     integer numMessagesQueued = llGetListLength(msgQueue);&lt;br /&gt;
     integer count = 0;&lt;br /&gt;
     integer done = FALSE;&lt;br /&gt;
     while (!done &amp;amp;&amp;amp; count &amp;amp;lt; numMessagesQueued) {&lt;br /&gt;
         nextMsg = llList2String(msgQueue, count);&lt;br /&gt;
         nextMsgSize = llStringLength(nextMsg);&lt;br /&gt;
 &lt;br /&gt;
         if (totalMsgSize + nextMsgSize &amp;amp;lt; MAX_SIZE_CHARS) {&lt;br /&gt;
             totalMsg += nextMsg;&lt;br /&gt;
             totalMsgSize += nextMsgSize;&lt;br /&gt;
             ++count;&lt;br /&gt;
         } else {&lt;br /&gt;
             done = TRUE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     // Delete the messages from the queue that we&#039;re going to send:&lt;br /&gt;
     if (count &amp;gt; 0) {&lt;br /&gt;
         msgQueue = llDeleteSubList(msgQueue, 0, count - 1);&amp;lt;/span&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return totalMsg;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;// Called when there are previous messages still queued, or if there&lt;br /&gt;
 // is no GET request currently open to respond to.&amp;lt;/span&amp;gt;&lt;br /&gt;
 //&lt;br /&gt;
 pushMessageToSend(string msg)&lt;br /&gt;
 {&lt;br /&gt;
     &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;msgQueue = msgQueue + [msg]; // last element is the last one stacked&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
     // See if we can send some messages now:&lt;br /&gt;
     &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;if (inId != NULL_KEY) {&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPResponse(inId, 200, popQueuedMessages());&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;inId = NULL_KEY;&amp;lt;/span&amp;gt;&lt;br /&gt;
     } // else wait for the next incoming GET request&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Replaces all occurrences of &#039;from&#039; with &#039;to&#039; in &#039;src&#039;&lt;br /&gt;
 // From http://snipplr.com/view/13279/lslstrreplace/&lt;br /&gt;
 //&lt;br /&gt;
 string str_replace(string subject, string search, string replace)&lt;br /&gt;
 {&lt;br /&gt;
     return llDumpList2String(llParseStringKeepNulls(subject, [search], []), replace);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Optionally filter out characters in text that would mess up the&lt;br /&gt;
 // web page display. This demo just escapes &#039; and &amp;quot; and adds a space after &#039;&amp;lt;&#039;.&lt;br /&gt;
 //&lt;br /&gt;
 string addSlashes(string s)&lt;br /&gt;
 {&lt;br /&gt;
     return str_replace(str_replace(str_replace(s, &amp;quot;&amp;amp;lt;&amp;quot;, &amp;quot;&amp;amp;lt; &amp;quot;), &amp;quot;\&amp;quot;&amp;quot;, &amp;quot;\\\&amp;quot;&amp;quot;), &amp;quot;&#039;&amp;quot;, &amp;quot;\\\&#039;&amp;quot;);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // This is the main interface for LSL to control the Shared Media web page.&lt;br /&gt;
 // The messages we send consist of Javascript function statements that the&lt;br /&gt;
 // browser will evaluate and execute in the context of the web page.&lt;br /&gt;
 // See sendMessageF() for a similar function with macro replacement.&lt;br /&gt;
 //&lt;br /&gt;
 sendMessage(string msg)&lt;br /&gt;
 {&lt;br /&gt;
     // Test for the easy case: if there are no other messages waiting&lt;br /&gt;
     // in the queue, and if there is an open GET connection, then just&lt;br /&gt;
     // respond immediately:&lt;br /&gt;
  &lt;br /&gt;
     &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;if (llGetListLength(msgQueue) == 0&amp;lt;/span&amp;gt; &amp;amp;&amp;amp; &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;inId != NULL_KEY) {&amp;lt;/span&amp;gt;&lt;br /&gt;
         // Nothing in the queue and an open GET, so respond immediately:&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPResponse(inId, 200, msg);&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;inId = NULL_KEY;&amp;lt;/span&amp;gt;&lt;br /&gt;
     } &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;else {&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;pushMessageToSend(msg);&amp;lt;/span&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Same as sendMessage() but with macro replacements. For each nth string&lt;br /&gt;
 // element in replacements, replace all occurrences of {@n}. For example,&lt;br /&gt;
 //     sendMessageF( &amp;quot;alert(&#039;{@0} {@1}!&#039;)&amp;quot;, [&amp;quot;Hello&amp;quot;, &amp;quot;World&amp;quot;] );&lt;br /&gt;
 // will send &amp;quot;alert(&#039;Hello World!&#039;)&amp;quot; to the web browser.&lt;br /&gt;
 //&lt;br /&gt;
 sendMessageF(string msg, list replacements)&lt;br /&gt;
 {&lt;br /&gt;
     integer numrepl = llGetListLength(replacements);&lt;br /&gt;
     integer i;&lt;br /&gt;
     for (i = 0; i &amp;amp;lt; numrepl; ++i) {&lt;br /&gt;
         msg = str_replace(msg, &amp;quot;{@&amp;quot; + (string)i + &amp;quot;}&amp;quot;, llList2String(replacements, i));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;sendMessage(msg);&amp;lt;/span&amp;gt;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E8E8FF&amp;quot;&amp;gt;// Chat logger demo: writes a new &amp;amp;lt;tr&amp;gt; table row to the web page&lt;br /&gt;
 // for every line of open chat it hears.&lt;br /&gt;
 //&lt;br /&gt;
 webAppInit()&lt;br /&gt;
 {&lt;br /&gt;
     string msg;&lt;br /&gt;
     string m0;&lt;br /&gt;
 &lt;br /&gt;
     // First, send over a few handy function definitions:&lt;br /&gt;
 &lt;br /&gt;
     msg = &amp;quot;function $$(t) { return document.getElementsByTagName(t)[0]; };&amp;quot;;&lt;br /&gt;
     msg += &amp;quot;function h() { return $$(&#039;head&#039;); };&amp;quot;;&lt;br /&gt;
     msg += &amp;quot;function b() { return $$(&#039;body&#039;); };&amp;quot;;&lt;br /&gt;
     msg += &amp;quot;function e(id) { return document.getElementById(id); };&amp;quot;;&lt;br /&gt;
     sendMessage(msg);&lt;br /&gt;
 &lt;br /&gt;
     // Send some CSS. WebKit is sensitive about appending &amp;amp;lt;style&amp;gt; elements&lt;br /&gt;
     // to &amp;amp;lt;head&amp;gt;, so we&#039;ll append it to an existing &amp;amp;lt;div&amp;gt; tag in &amp;amp;lt;body&amp;gt; instead.&lt;br /&gt;
 &lt;br /&gt;
     msg = &amp;quot;e(&#039;dv&#039;).innerHTML += \&amp;quot;{@0}\&amp;quot;;&amp;quot;;&lt;br /&gt;
     m0 = &amp;quot;&amp;amp;lt;style&amp;gt;td:nth-child(2) { text-align:right } tr:nth-child(odd) { background-color:#f8e8f8 }&amp;amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
     sendMessageF(msg, [m0]);&lt;br /&gt;
 &lt;br /&gt;
     // Write a &amp;amp;lt;table&amp;gt; element into element div#dv. The lines of chat will&lt;br /&gt;
     // become rows in this table appended to tbody#tbd&lt;br /&gt;
 &lt;br /&gt;
     msg = &amp;quot;e(&#039;dv&#039;).innerHTML += \&amp;quot;{@0}\&amp;quot;;&amp;quot;;&lt;br /&gt;
     m0 = &amp;quot;&amp;amp;lt;table&amp;gt;&amp;amp;lt;tbody id=&#039;tbd&#039;&amp;gt;&amp;amp;lt;/tbody&amp;gt;&amp;amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
     sendMessageF(msg, [m0]);&lt;br /&gt;
 &lt;br /&gt;
     llListen(0, &amp;quot;&amp;quot;, NULL_KEY, &amp;quot;&amp;quot;);&amp;lt;/span&amp;gt;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llClearPrimMedia(face);&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;        llRequestURL();&amp;lt;/span&amp;gt;&lt;br /&gt;
          &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E8E8FF&amp;quot;&amp;gt;        webAppInit();&amp;lt;/span&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;    http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             myURL = body;&lt;br /&gt;
             llOwnerSay(&amp;quot;myURL=&amp;quot; + myURL);&lt;br /&gt;
             setDataURI(myURL);&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             // Either send some queued messages now with llHTTPResponse(),&lt;br /&gt;
             // or if there&#039;s nothing to do now, save the GET id and&lt;br /&gt;
             // wait for somebody to call sendMessage().&amp;lt;/span&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;            if (llGetListLength(msgQueue) &amp;gt; 0) {&amp;lt;/span&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;                llHTTPResponse(id, 200, popQueuedMessages());&lt;br /&gt;
                 inId = NULL_KEY;&lt;br /&gt;
             } else {&lt;br /&gt;
                 inId = id;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E8E8FF&amp;quot;&amp;gt;    // When we hear chat from name, send a Javascript statement that&lt;br /&gt;
     // appends HTML to element #tbd. I.e., we&#039;ll make a string of HTML&lt;br /&gt;
     // formatted like this:&lt;br /&gt;
     //      &amp;amp;lt;tr style=&amp;quot;color:hsl(200,100%,30%)&amp;quot;&amp;gt;&lt;br /&gt;
     //         &amp;amp;lt;td&amp;gt;[01:23]&amp;amp;lt;/td&amp;gt;&lt;br /&gt;
     //         &amp;amp;lt;td&amp;gt;Avatar Name&amp;amp;lt;/td&amp;gt;&lt;br /&gt;
     //         &amp;amp;lt;td&amp;gt;the chat text&amp;amp;lt;/td&amp;gt;&lt;br /&gt;
     //      &amp;amp;lt;/tr&amp;gt;&lt;br /&gt;
     // and send it wrapped it in a Javascript statement like this:&lt;br /&gt;
     //      e(&#039;tbd&#039;).innerHTML += htmlstring;&lt;br /&gt;
     //&lt;br /&gt;
     listen(integer chan, string name, key id, string chat)&lt;br /&gt;
     {&lt;br /&gt;
         integer s = (integer)(&amp;quot;0x&amp;quot; + llGetSubString((string)id, 0, 6));&lt;br /&gt;
         string hue = (string)(s % 360);&lt;br /&gt;
         string color = &amp;quot;hsl(&amp;quot; + hue + &amp;quot;,100%, 30%)&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         string msg = &amp;quot;e(&#039;tbd&#039;).innerHTML += &#039;{@0}&#039;;&amp;quot;;&lt;br /&gt;
         string m0 = &amp;quot;&amp;amp;lt;tr style=\&amp;quot;color: {@4}\&amp;quot;&amp;gt;&amp;quot;;&lt;br /&gt;
         m0 +=   &amp;quot;&amp;amp;lt;td&amp;gt;{@1}&amp;amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
         m0 +=   &amp;quot;&amp;amp;lt;td&amp;gt;{@2}:&amp;amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
         m0 +=   &amp;quot;&amp;amp;lt;td&amp;gt;{@3}&amp;amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
         m0 += &amp;quot;&amp;amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         string t = llGetSubString(llGetTimestamp(), 11, 15);&lt;br /&gt;
         sendMessageF(msg, [m0, &amp;quot;[&amp;quot; + t + &amp;quot;]&amp;quot;, name, addSlashes(chat), color]);&lt;br /&gt;
     }&amp;lt;/span&amp;gt;&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
&amp;lt;ol start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;This &amp;quot;[http://en.wikipedia.org/wiki/Push_technology server-push]&amp;quot; technique using long-polling gives an LSL script the ability to modify the web page displayed on a prim. The Javascript side always keeps an HTTP GET open to the prim&#039;s HTTP-in URL, and the LSL side responds whenever it wants to with a response consisting of strings of Javascript to be executed by the web browser. For example, this LSL code causes an alert box to pop up on the web page:&lt;br /&gt;
: &amp;lt;code&amp;gt;sendMessage( &amp;quot;alert()&amp;quot; );&amp;lt;/code&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;In the LSL listing above, &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;the yellow highlighting&amp;lt;/span&amp;gt; shows the most important parts of the HTTP long-polling mechanism. The &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;green highlighting&amp;lt;/span&amp;gt; shows the message buffering on top of that. The &amp;lt;span style=&amp;quot;background-color:#E8E8FF&amp;quot;&amp;gt;blue highlighting&amp;lt;/span&amp;gt; is the application code that uses long-polling. To use long-polling for a different application, replace the blue parts. The rest is glue.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;To avoid some subtle WebKit problems when dynamically appending or replacing the first-level child nodes in &amp;amp;lt;head&amp;gt; or &amp;amp;lt;body&amp;gt;, we&#039;ve made two special &amp;amp;lt;div&amp;gt; elements on the bootstrap HTML page. The first surrounds the script#sc element that we replace for each GET. The second is at the end of &amp;amp;lt;body&amp;gt; as a convenient place for the application to insert new content that would otherwise go in &amp;amp;lt;body&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;To make sure that there is nearly always a valid GET request open, the Javascript side starts a new GET after receiving a response, or just before the last unanswered GET times out.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;There are two timeouts hard-coded in the bootstrap data URI:&lt;br /&gt;
&amp;lt;ol style=&amp;quot;list-style-type:lower-roman&amp;quot;&amp;gt;&amp;lt;li&amp;gt;The &amp;quot;20000&amp;quot; is the timeout (20 seconds) for when an open GET expires, and should be set to a little less than the minimum of the WebKit outgoing GET request timeout, and the SL-LSL incoming GET timeout. This ensures that GETs are nearly continuous, and the LSL side can always respond to the most recent GET request. [[LlHTTPResponse|This page]] says the timeout on the LSL side is 25 seconds.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;The &amp;quot;500&amp;quot; is a throttle that sets an upper limit to how fast the Javascript re-polls HTTP-in to prevent hammering the simulator. It&#039;s a half-second delay after receiving a GET response before starting a new GET.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; If there&#039;s a little gap between two GETs, or if two overlap a little, the sendMessage() message buffering will compensate by queuing up messages to be sent when the next GET arrives. If there are multiple messages in the queue when a GET arrives, the LSL side will concatenate as many messages as possible and send them together.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;On the Javascript side, the GET is not triggered until the &amp;lt;tt&amp;gt;&amp;amp;lt;script&amp;gt;&amp;lt;/tt&amp;gt; element is attached to its parent node with .appendChild() or .replaceChild().&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;The bootstrap Javascript above in setDataURI() had to be compressed somewhat to fit into the 1024-byte data URI limit. Here&#039;s an expanded listing for reference with more descriptive names:&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 &amp;amp;lt;!DOCTYPE HTML&amp;gt;&lt;br /&gt;
 &amp;amp;lt;html&amp;gt;&lt;br /&gt;
   &amp;amp;lt;body&amp;gt;&lt;br /&gt;
     &amp;amp;lt;div&amp;gt;&lt;br /&gt;
       &amp;amp;lt;script id=&#039;script&#039;&amp;gt; &amp;amp;lt;/script&amp;gt;&lt;br /&gt;
     &amp;amp;lt;/div&amp;gt;&lt;br /&gt;
     &amp;amp;lt;script&amp;gt;&lt;br /&gt;
       var poll=function(){&lt;br /&gt;
         var script=document.getElementById(&#039;script&#039;),&lt;br /&gt;
         timeoutId,&lt;br /&gt;
         seq=0,&lt;br /&gt;
         newScript;&lt;br /&gt;
         return {&lt;br /&gt;
           beg:function() {                                      // Initiate a long-poll GET&lt;br /&gt;
             newScript=document.createElement(&#039;script&#039;);        // The response will go here&lt;br /&gt;
             newScript.onload=poll.end;                         // Call poll.end() when we get a response&lt;br /&gt;
             timeoutId=setTimeout(&#039;poll.end()&#039;,20000);          // ... or if we time out&lt;br /&gt;
             newScript.src=&#039; HTTP-in URL goes here /?r=&#039;+(seq++);&lt;br /&gt;
             script.parentNode.replaceChild(newScript,script);  // this triggers the GET&lt;br /&gt;
             script=newScript;},&lt;br /&gt;
 &lt;br /&gt;
           end:function() {&lt;br /&gt;
             clearTimeout(timeoutId);&lt;br /&gt;
             timeoutId=null;&lt;br /&gt;
             script.onload=null;&lt;br /&gt;
             setTimeout(&#039;poll.beg()&#039;,500);                      // Wait a bit before re-polling&lt;br /&gt;
           },&lt;br /&gt;
         };&lt;br /&gt;
       }();&lt;br /&gt;
     &amp;amp;lt;/script&amp;gt;&lt;br /&gt;
     &amp;amp;lt;button id=&#039;btn&#039;onclick=poll.beg()&amp;gt;Start&amp;amp;lt;button&amp;gt;&lt;br /&gt;
     &amp;amp;lt;div id=&#039;dv&#039;&amp;gt; &amp;amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;amp;lt;/body&amp;gt;&lt;br /&gt;
 &amp;amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Make TinyURLs by script===&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 string myTinyURL;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             // Send our full URL to tinyurl.com for conversion&lt;br /&gt;
             // The answer will come back in http_response()&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPRequest(&amp;quot;http://tinyurl.com/api-create.php?url=&amp;quot; + body, [], &amp;quot;&amp;quot;);&amp;lt;/span&amp;gt;&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             llHTTPResponse(id, 200, &amp;quot;Hello Real World from the Virtual World&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_response(key req, integer stat, list met, string body)&lt;br /&gt;
     {&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;myTinyURL = body;&amp;lt;/span&amp;gt;&lt;br /&gt;
         llOwnerSay(&amp;quot;My HTTP-in TinyURL is: &amp;quot; + myTinyURL + &amp;quot; , Click Me!&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* Any URL or data URI can be mapped to a TinyURL. The example above reduces an assigned HTTP-in URL, which is a long one such as:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://sim4605.agni.lindenlab.com:12046/cap/2b9f06f7-431e-5b0f-9271-2d03bd15370b&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
into a TinyURL this size:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://tinyurl.com/y9etul3&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Development hints===&lt;br /&gt;
# You can develop your HTML and JavaScript outside of Second Life by putting your JavaScript incantations in data URIs that you feed to any web browser. For the most compatible browsers, use Safari or Google Chrome, or any other that uses the [[QtWebKit|WebKit]] HTML rendering engine. If you can&#039;t run Safari or Chrome, try the open source [http://code.google.com/p/arora/ Arora web browser] for simulating Media-on-a-Prim: it&#039;s based on straight-up WebKit and has the same handy inspector/debugger found in other WebKit browsers.&lt;br /&gt;
# If you don&#039;t have direct access to a web server while developing this stuff, then consider installing a local copy of [http://www.apache.org/ Apache web server] on your computer. You can restrict it to local access. Then you can refer to files on your own computer with URLs that start with &amp;lt;tt&amp;gt;&amp;lt;nowiki&amp;gt;http://localhost/&amp;lt;/nowiki&amp;gt;...&amp;lt;/tt&amp;gt;, and PHP scripts can simulate what the HTTP-in server would serve. That lets you, for example, edit &amp;lt;tt&amp;gt;myfile.js&amp;lt;/tt&amp;gt; on your own computer while working on a data URI that contains &amp;lt;tt&amp;gt;&amp;amp;lt;script src=&amp;quot;&amp;lt;nowiki&amp;gt;http://localhost/myfile.js&amp;lt;/nowiki&amp;gt;&amp;quot;&amp;gt;&amp;lt;/tt&amp;gt;. Then when &amp;lt;tt&amp;gt;myfile.js&amp;lt;/tt&amp;gt; is working, you can put the debugged code into an LSL script that will serve it through its HTTP-in port. That&#039;s easier than debugging all that in-world.&lt;br /&gt;
&lt;br /&gt;
===References and sources===&lt;br /&gt;
* [[User:Kelly_Linden/lsl_hacks|Kelly Linden&#039;s hacks]]&lt;br /&gt;
* [http://talirosca.wikidot.com/bootstrapping-html-on-a-prim Tali Rosca&#039;s techniques]&lt;br /&gt;
* [https://blogs.secondlife.com/message/115303 Notecard Text on a Prim - Demo]&lt;br /&gt;
* [[llSetPrimMediaParams]]()&lt;br /&gt;
* {{JIRA|SVC-3427}}&lt;br /&gt;
* [https://blogs.secondlife.com/message/124144 Ajax in Shared Media #1]&lt;br /&gt;
* [https://blogs.secondlife.com/thread/11698 Ajax in Shared Media #2]&lt;br /&gt;
* [https://blogs.secondlife.com/thread/12727 Ajax in Shared Media #3]&lt;br /&gt;
* [https://blogs.secondlife.com/thread/13455 SVG (Scalable Vector Graphics) in Shared Media]&lt;br /&gt;
* [[Shared_Media_and_data_URI|Shared_Media_and_data_URI]]&lt;br /&gt;
* [https://blogs.secondlife.com/community/community/tnt/blog/2010/03/17/viewer-2-tip-shared-media-add-custom-content-with-data-uri-no-webpage-upload-needed Shared Media: Add custom content with data: URI]&lt;br /&gt;
* [https://blogs.secondlife.com/message/119704 &amp;quot;Shared Media&amp;quot; - A few facts]&lt;br /&gt;
&lt;br /&gt;
[[Category:Shared Media]]&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/New_LSL_Functions&amp;diff=883203</id>
		<title>User:Becky Pippen/New LSL Functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/New_LSL_Functions&amp;diff=883203"/>
		<updated>2010-04-27T18:08:15Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: tweak wording to show that server 1.38 is here.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== New LSL Functions ==&lt;br /&gt;
&lt;br /&gt;
These memory-saving LSL functions are now available in server version 1.38::&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; list llGetLinkPrimitiveParams(integer linknumber, list params)&lt;br /&gt;
 &lt;br /&gt;
      llSetLinkPrimitiveParamsFast(integer link, list params)&lt;br /&gt;
 &lt;br /&gt;
      llLinkParticleSystem(integer link, list rules)&lt;br /&gt;
 &lt;br /&gt;
      llSetLinkTextureAnim(integer mode, integer face, integer sizex, integer sizey, float start, float length, float rate)&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will also get [[PRIM_TEXT]] for getting and setting hover text with [[llSetLinkPrimitiveParams]]().&lt;br /&gt;
The parameter list will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; PRIM_TEXT, string msg, vector color, float alpha &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===The deal with resizer scripts===&lt;br /&gt;
&lt;br /&gt;
Some content creators prefer to distribute their objects no-mod, yet still allow customers to resize or&lt;br /&gt;
retexture the linkset. Scripts can do that in a no-mod linkset, but the obvious solution using&lt;br /&gt;
[[llSetLinkPrimitiveParams]]() doesn&#039;t work well because of the 0.2-second delay built into the function.&lt;br /&gt;
To work around the delay, we have had to put a script in every child prim that can respond immediately&lt;br /&gt;
to a message from the root prim. When these functions become available, we will be able to do this kind of thing with a single script.&lt;br /&gt;
&lt;br /&gt;
===Fixing existing scripts===&lt;br /&gt;
&lt;br /&gt;
To fix such a script, locate the call to [[llMessageLinked]]() in the master script where it sends the link&lt;br /&gt;
message to the individual prims. It might look something vaguely like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llMessageLinked(LINK_whatever, command_resize, (string)newSize, NULL_KEY);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Replace that with a loop that applies whatever changes you need to each prim using&lt;br /&gt;
[[llSetLinkPrimitiveParamsFast]](). The prims have link numbers from 1 through [[llGetNumberOfPrims]](),&lt;br /&gt;
so you can make an efficient loop as shown here; just add any additional processing you&lt;br /&gt;
need for each prim inside the loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; applyChildParams(list ruleSet)&lt;br /&gt;
 {&lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum, ruleSet);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, to use this function to set the color and alpha of all the prims, build the parameter&lt;br /&gt;
list and pass it to applyChildParams() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;applyChildParams([PRIM_COLOR, ALL_SIDES, &amp;lt;0.9, 0.8, 0.2&amp;gt;, 1.0]);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
However, when you only need to apply a constant rule set like this to every prim in the linkset,&lt;br /&gt;
then the loop above can be simplified to a single statement;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llSetLinkPrimitiveParamsFast(LINK_SET, ruleSet);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or use [[LINK_ALL_OTHERS]] or [[LINK_ALL_CHILDREN]] instead of [[LINK_SET]] as appropriate.&lt;br /&gt;
&lt;br /&gt;
For another example, you can make a resizer script apply relative instead of absolute size changes by&lt;br /&gt;
using the new function [[llGetLinkPrimitiveParams]](). The script can read each child&#039;s current size,&lt;br /&gt;
then apply a resize factor and use [[llSetLinkPrimitiveParamsFast]]() to set a new size. This can help&lt;br /&gt;
make it easy for the script to do the right thing even if reset and all its variables get reinitialized. The&lt;br /&gt;
following function will resize all the prims (including the root itself) by a factor:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; rescaleLinksetByFactor(float factor)&lt;br /&gt;
 {&lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list params = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE]);&lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum,&lt;br /&gt;
             [PRIM_SIZE, factor * llList2Vector(params, 0)]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But what if the function above encounters a prim that, after resized, would have a dimension outside&lt;br /&gt;
the legal range of 0.01m to 10.0m? That could have unfortunate consequences, so&lt;br /&gt;
here is a function that resizes all the prims by a specified factor but only if all the resulting&lt;br /&gt;
dimensions in all the prims will all be in the legal range of 0.01m to 10.0m. If any dimension in&lt;br /&gt;
any prim would go out of those bounds, the function will return FALSE without resizing anything.&lt;br /&gt;
This example also includes repositioning the child prims within the linkset. Change or add whatever&lt;br /&gt;
additional parameters are needed for your particular application.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // This is not a complete resizer solution; it&#039;s just a demo of&lt;br /&gt;
 // how to call the new LSL functions.&lt;br /&gt;
 &lt;br /&gt;
 // Set the minimum and maximum size allowed for any prim in the linkset:&lt;br /&gt;
 //&lt;br /&gt;
 float minPrimSize = 0.01;&lt;br /&gt;
 float maxPrimSize = 10.0;&lt;br /&gt;
 &lt;br /&gt;
 // Resize all the prims in the linkset. For example, to resize all&lt;br /&gt;
 // the prims to 1% smaller, call this function with factor=0.99&lt;br /&gt;
 // Returns TRUE if successful, or FALSE if resizing would have&lt;br /&gt;
 // set a prim size out of bounds.&lt;br /&gt;
 //&lt;br /&gt;
 integer resizeLinksetWithBounds(float factor)&lt;br /&gt;
 {&lt;br /&gt;
     // Test to see if it&#039;s safe to change all the prim sizes:&lt;br /&gt;
 &lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list params = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE]);&lt;br /&gt;
         vector sz = factor * llList2Vector(params, 0);&lt;br /&gt;
         if (sz.x &amp;lt; minPrimSize || sz.x &amp;gt; maxPrimSize ||&lt;br /&gt;
             sz.y &amp;lt; minPrimSize || sz.y &amp;gt; maxPrimSize ||&lt;br /&gt;
             sz.z &amp;lt; minPrimSize || sz.z &amp;gt; maxPrimSize) {&lt;br /&gt;
                 return FALSE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     // We&#039;ve determined that we can safely change all the sizes, so&lt;br /&gt;
     // let&#039;s do so now.&lt;br /&gt;
 &lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list oldParams = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE, PRIM_POSITION]);&lt;br /&gt;
 &lt;br /&gt;
         list newParams = [ PRIM_SIZE, factor * llList2Vector(oldParams, 0) ];&lt;br /&gt;
         if (linkNum &amp;gt; 1) {&lt;br /&gt;
             vector linkPos = (vector)llList2String(oldParams, 1);&lt;br /&gt;
             vector localPos = (linkPos - llGetRootPosition()) / llGetRootRotation();&lt;br /&gt;
             newParams += [ PRIM_POSITION, factor * localPos ];&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum, newParams);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return TRUE;&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&#039;s a silly example of how it&#039;s used:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Example of how to call resizeLinksetWithBounds()&lt;br /&gt;
 //&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         float factor = llPow(llFrand(2.0), 2.0); // random factor for demo&lt;br /&gt;
         if (resizeLinksetWithBounds(factor)) {&lt;br /&gt;
             llOwnerSay(&amp;quot;Resized successfully by factor = &amp;quot; + (string)factor);&lt;br /&gt;
         } else {&lt;br /&gt;
             llOwnerSay(&amp;quot;Oops, can&#039;t resize by factor &amp;quot; + (string)factor);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
(Acknowledgements: Thanks to Keith Reinard for testing and improving the example above.)&lt;br /&gt;
&lt;br /&gt;
Also see Maestro Linden&#039;s newly posted [[Linkset_resizer|linkset resizer]].&lt;br /&gt;
&lt;br /&gt;
===Resizer challenges===&lt;br /&gt;
&lt;br /&gt;
A complete resizer solution is outside the scope of this article, but might need to consider such things as:&lt;br /&gt;
* You might need a way to reset all the prims to a known size, position, and orientation in case a resize loop breaks before successfully resizing all the prims.&lt;br /&gt;
* The example above might give unexpected results if executed while the object is in motion. For example, there is no way to guarantee that the functions llGetLinkPrimitiveParams() and llGetRootPosition() will be evaluated during the same simulator frame, so the local offset of a child prim could be computed incorrectly as the difference of the root position in one frame and the child position in another frame.&lt;br /&gt;
* Roundoff errors could accumulate if proportional scaling is done &#039;&#039;many&#039;&#039; times. (See [[User_talk:Becky_Pippen/New_LSL_Functions|the Discussion page]].)&lt;br /&gt;
* It&#039;s easy to scale a linkset so that prims get moved beyond their [[Linkability_Rules|linkability limits]], and no easy way for a script to test for this in advance. For more information, see the discussions at {{JIRA|SVC-5328}} and {{JIRA|SVC-5166}}.&lt;br /&gt;
&lt;br /&gt;
===Long range plans===&lt;br /&gt;
&lt;br /&gt;
The new LSL features described above are expected to appear in server version 1.38. Babbage Linden also mentioned a few additional LSL features he would like to implement sometime after 1.38, but with no guarantees if or when:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; PRIM_NAME, PRIM_DESC  // for llGet/SetPrimitiveParams()&lt;br /&gt;
 llAvatarOnLinkSitTarget()&lt;br /&gt;
 llLinkSitTarget() &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Also see===&lt;br /&gt;
&lt;br /&gt;
* [[Babbage_Linden|Babbage Linden at his Office Hours]]&lt;br /&gt;
* [https://blogs.secondlife.com/community/technology/blog/2010/03/05/server-138-beta-now-open this SL Blog announcement]&lt;br /&gt;
* [[User:Becky_Pippen/Script_Memory_Limits| Scripter&#039;s checklist of things you can do to conserve memory]]&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Limits_FAQ| Script memory limits FAQ]]&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Memory_Limits_FAQ&amp;diff=883193</id>
		<title>User:Becky Pippen/Memory Limits FAQ</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Memory_Limits_FAQ&amp;diff=883193"/>
		<updated>2010-04-27T17:59:48Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Tweak wording to indicate server 1.38 has been deployed.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== FAQ - Script Memory Limits ==&lt;br /&gt;
&lt;br /&gt;
For the most official announcement so far, see [https://blogs.secondlife.com/community/land/blog/2010/03/19/the-first-step-toward-script-limits here].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Why do we need script memory limits?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; In some regions, the simulator uses so much script memory that it has to swap virtual memory to disk. That causes major lag, and lag is among the top complaints about Second Life.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; How can I tell if I&#039;m a heavy script user?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Starting with server version 1.38 and viewer version 2.0, we can now see our individual script memory usage, similar to how estate managers can see top scripts in a region. Also scripters can use the function [[LlGetFreeMemory|llGetFreeMemory()]] to get an approximate idea of memory usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will this break content?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Yes, some. The limits are specifically designed to limit the worst 5% of memory consumers so that the remaining 95% can continue to run freely without causing the simulator to swap virtual memory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; When will all this happen?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Script memory reporting and the new memory-efficient LSL functions are now available on the main grid using server version 1.38 and version 2.0 of the SL viewer. Server version 1.40 or later will have &amp;quot;Small Scripts&amp;quot; capability (see below). Memory limits will not be enforced until later in 2010 after these features have been deployed on the main grid for some time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will the limits be per avatar or per region?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; There will be limits on avatar, parcel, and region in a way that parallels [[LSL_http_server#Resource_Limitations|how URL resources are managed]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will Linden Lab pay for all the scripted products we bought that will no longer work?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Most products are reasonably scripted and will continue to work just fine. The only problematic products will be unusual ones that are scripted in ways that use unreasonable amounts of RAM. Try to get the product creator to make a new version that uses less memory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will we see a reduction in lag across the grid?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; It won&#039;t have a major impact on most regions. However, it will make a huge improvement on the 5% of the regions that use so much script memory that the server has to swap virtual memory to disk. It is also expected to improve performance on any other simulators running on the same server host using the same disk resources.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; How much RAM are we talking about?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; In the 95% of the regions that run smoothly, the simulator uses less than 800MB of RAM for everything except scripts, and up to 300MB for all the scripts in the region, and that&#039;s ok.  In the worst 5% of the regions, simulators use up to 2GB of RAM on the server, and that&#039;s not ok.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Why not just add RAM to the servers? RAM is cheap.&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Several thousand Xeon server boards at, say, US$100 each to upgrade, comes out to a bunch of money, even for Linden Lab. Moving forward, new simulators (class 7) will have more RAM.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Who will be affected?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; All scripts, old and new, will be subject to the new memory limits. When planning for the memory limits, [http://wiki.secondlife.com/wiki/User:Babbage_Linden/Office_Hours/2009_11_25 Babbage Linden said], &amp;quot;[2009/11/25 3:43]  Babbage Linden: ideally we&#039;d like to have 95+% of people use scripts as they currently do while still having 95+% of simulators running without swapping&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What&#039;s the difference between &amp;quot;Small Scripts Project,&amp;quot; &amp;quot;Big Scripts Project,&amp;quot; and &amp;quot;Efficient Scripts Project?&amp;quot;&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; The &#039;&#039;Efficient Scripts Project&#039;&#039; is the overall effort to reduce the memory consumption of Mono-based scripts, including limits on memory usage. The &#039;&#039;Small Scripts Project&#039;&#039; and &#039;&#039;Big Scripts Project&#039;&#039; both refer to a refinement where each script can request the specific amount of memory it anticipates using instead of being charged with a constant 16KB for LSL or 64KB for Mono scripts. So, for example, a simple Mono sit script might request only 3KB while another data-intensive Mono script might request several megabytes. Since the average Mono script in SL needs only about 9KB, this could help reduce overall script memory usage in a typical region. In [http://wiki.secondlife.com/wiki/User:Babbage_Linden/Office_Hours/2010_03_03 Office Hours], Babbage Linden said, &amp;quot;I would like to ship script usage, then small scripts, then script limits, then big scripts... so it would be nice to have mono scripts be able to reserve a lower memory amount before script limits enforcement happens.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What&#039;s the &amp;quot;LSL Penalty?&amp;quot;&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; At one time, LL considered charging LSL scripts with more memory than they actually consume as an incentive for scripters to use Mono. That idea has been dropped, and the current plan is to charge all scripts with the actual memory they reserve — currently 16KB for LSL, and 64KB for Mono scripts. In the future after the Small Scripts and Big Scripts Project have been implemented, Mono scripts can be charged for only the amount of memory they reserve, which could be more or less than 64KB. Discussion of this can be found in the [http://lists.secondlife.com/pipermail/opensource-dev/ opensource-dev] mailing list.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What can I do as a content creator to prepare?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Be kind to your customers and give them product updates using the [[User:Becky_Pippen/New_LSL_Functions| new efficient LSL functions]]. Learn more about how to [[User:Becky_Pippen/Measure_Script_Memory_Usage|measure memory usage]] and the [[User:Becky_Pippen/Script_Memory_Limits|scripting techniques for reducing memory]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What can I do as a customer?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; 95% of scripts are just fine and you don&#039;t need to do anything. If you are wearing hair, shoes, or other attachments with a script in every prim, then you can contact the creator and ask if there is a product update that uses the new LSL memory-efficient functions.&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=Talk:Code_Sizer&amp;diff=881122</id>
		<title>Talk:Code Sizer</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=Talk:Code_Sizer&amp;diff=881122"/>
		<updated>2010-04-26T18:31:08Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= MONO v LSO =&lt;br /&gt;
&lt;br /&gt;
I noticed [[Code Sizer|this script]] was written for use with LSO (no mention of that fact though (unless I am missing it)) so I set about writing a version to handle both LSO and MONO. Yet again I find myself pulling faces at the screen whilst trying to make sense of MONO&#039;s free memory. I came up with this -&lt;br /&gt;
&amp;lt;lsl&amp;gt;GetBytes(integer type)&lt;br /&gt;
{&lt;br /&gt;
    integer this; // How many bytes are used (minimum) to compile and run.&lt;br /&gt;
    if(type == 64) this = 3844; // This amount is hard to reduce in mono&lt;br /&gt;
    else this = 354; // Slight changes have big effects in LSO&lt;br /&gt;
    llOwnerSay((string)((1024 * type) - (llGetFreeMemory() + this)));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
default&lt;br /&gt;
{&lt;br /&gt;
    state_entry()&lt;br /&gt;
    {&lt;br /&gt;
        GetBytes(16); // 1 simple call per test.&lt;br /&gt;
        // Although (in mono) it is very hard to tell exactly how many bytes are used by each call of GetBytes()&lt;br /&gt;
        // One thing is for certain...Mono is odd.&lt;br /&gt;
        &lt;br /&gt;
        // An example of code you might test -&lt;br /&gt;
        &lt;br /&gt;
        llOwnerSay(llDumpList2String([&amp;quot;is&amp;quot;, &amp;quot;how&amp;quot;, &amp;quot;much&amp;quot;, &amp;quot;does&amp;quot;, &amp;quot;this&amp;quot;, &amp;quot;code&amp;quot;, &amp;quot;take&amp;quot;, &amp;quot;up&amp;quot;, &amp;quot;in&amp;quot;, &amp;quot;bytes?&amp;quot;], &amp;quot; &amp;quot;));&lt;br /&gt;
        &lt;br /&gt;
        // The llOwnerSay() above takes 512 bytes in mono and 122 in LSO&lt;br /&gt;
        &lt;br /&gt;
        // When a script is first compiled in mono there is a larger byte count used than after all subsequent resets.&lt;br /&gt;
        // On the first run the state_entry llOwnerSay() code takes 572 bytes. After reset 512.&lt;br /&gt;
        // LSO does not have this initial discrepancy.&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/lsl&amp;gt;&lt;br /&gt;
I am sure someone else can do better. What interests me is how we should accurately measure memory use when [[llGetFreeMemory]] returns some very odd results (if you play around with enough variants for long enough). I wonder if someone wise might share? -- &#039;&#039;&#039;[[User:Fred_Gandt|Fred Gandt]]&#039;&#039;&#039; &amp;lt;sup&amp;gt;&amp;lt;small&amp;gt;([[User talk:Fred_Gandt|talk]]|[[Special:Contributions/Fred_Gandt|contribs]])&amp;lt;/small&amp;gt;&amp;lt;/sup&amp;gt; 23:57, 25 April 2010 (UTC)&lt;br /&gt;
: There are a few more clues and links about Mono&#039;s memory management, including a possible explanation of the 512-byte change you saw, at [[User:Becky_Pippen/Measure_Script_Memory_Usage|this page]]. Also, [[User:Becky_Pippen/LSL_Performance|this page]] shows examples of the Mono instructions that the compiler generates for an LSL function call.  -- [[User:Becky Pippen|Becky Pippen]] 18:31, 26 April 2010 (UTC)&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Shared_Media_LSL_Recipes&amp;diff=876003</id>
		<title>User:Becky Pippen/Shared Media LSL Recipes</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Shared_Media_LSL_Recipes&amp;diff=876003"/>
		<updated>2010-04-23T06:31:09Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: remove chars that made the TOC look weird&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==How-to&#039;s with Media-on-a-Prim==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
  MediaWiki issue: If anyone knows how to highlight parts of code inside the &amp;lt;lsl&amp;gt;&lt;br /&gt;
  wrapper, let me know. I resorted to using a style attribute in a &amp;lt;span&amp;gt; element&lt;br /&gt;
  inside a &amp;lt;tt&amp;gt; element to yellow-highlight preformatted monospace text. It would&lt;br /&gt;
  be nicer if we could also get the LSL syntax highlighting, but if we have to choose&lt;br /&gt;
  one or the other, I think the yellow highlighting is more useful and instructive&lt;br /&gt;
  for this particular page than LSL syntax highlighting.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The examples here assume you&#039;re comfortable with LSL, and you just need a quick reference for the syntax of handling HTML and JavaScript in LSL for Shared Media scripting. All the examples on this page are ready to copy-n-paste and drop into a prim — no other setup needed. The key syntax in each example is highlighted. There&#039;s no error handling in these minimalist examples, so add plenty of your own.&lt;br /&gt;
&lt;br /&gt;
===Display plain text — XyText replacement===&lt;br /&gt;
[[Image:MoaP-example_1.png‎|thumb|Plain text]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;integer face = 4;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string message = &amp;quot;Hello World&amp;quot;;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llSetPrimMediaParams(face,&amp;lt;/span&amp;gt;&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;[PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message]);&amp;lt;/span&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* An avatar must press the Reload button on the browser to see this rendered in-world. A page will be auto-loaded if you include the parameter &amp;lt;tt&amp;gt;PRIM_MEDIA_AUTO_PLAY, TRUE&amp;lt;/tt&amp;gt;, and if the avatar has auto-load enabled in preferences.&lt;br /&gt;
* There&#039;s no need to add any %-hex-escaping or to use [[llEscapeURL]](); llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, url]) will automatically escape the string for you.&lt;br /&gt;
* This works by feeding a data URI to the on-prim browser. Think of it as serving the contents of the page in the URL itself. For more information, see [[Shared_Media_and_data_URI|here]] and [http://en.wikipedia.org/wiki/Data_URI_scheme here].&lt;br /&gt;
* The in-world browser limits the data URI (or any address) to 1024 bytes after %-hex-escaping and encoded in UTF-8. See below for tricks using external or self-served JavaScript or CSS to leverage the data URI.&lt;br /&gt;
* If the data URI contains just plain text, the prefix &amp;lt;tt&amp;gt;&amp;quot;data:text/html,&amp;quot;&amp;lt;/tt&amp;gt; can be abbreviated &amp;lt;tt&amp;gt;&amp;quot;data:,&amp;quot;&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Force a page reload===&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
[[Image:MoaP-example-refreshpage.gif|thumb|Auto-refresh]]&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string myURL;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;integer seq = 0; // sequence number for unique URLs&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             myURL = body;&lt;br /&gt;
             llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_AUTO_PLAY, TRUE,&lt;br /&gt;
                  PRIM_MEDIA_CURRENT_URL, myURL]);&lt;br /&gt;
             llSetTimerEvent(5.0);&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             llHTTPResponse(id, 200, &amp;quot;Sim FPS: &amp;quot; + (string)llGetRegionFPS());&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     timer()&lt;br /&gt;
     {&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_CURRENT_URL, myURL + &amp;quot;/?r=&amp;quot; + (string)(++seq)&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
Comments:&lt;br /&gt;
* The page will reload automatically if &amp;lt;tt&amp;gt;PRIM_MEDIA_AUTO_PLAY&amp;lt;/tt&amp;gt; is enabled and if the new &amp;lt;tt&amp;gt;PRIM_MEDIA_CURRENT_URL&amp;lt;/tt&amp;gt; is different. This technique appends a short dummy parameter (like &amp;lt;tt&amp;gt;&amp;quot;/?r=12&amp;quot;&amp;lt;/tt&amp;gt;) to the end of the URL to make it different each time, forcing a reload. The parameter is otherwise ignored.&lt;br /&gt;
&lt;br /&gt;
===Display text with HTML markup===&lt;br /&gt;
[[Image:MoaP-example-markup.png‎‎|thumb|HTML markup]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string message = &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;quot;&amp;amp;lt;i&amp;gt;Hello&amp;amp;lt;/i&amp;gt;&amp;amp;lt;h2&amp;gt;World!&amp;amp;lt;/h2&amp;gt;&amp;quot;&amp;lt;/span&amp;gt;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* The text of the data URI will be %-hex-escaped automatically for you, so there&#039;s no need to use [[llEscapeURL]](). All you need in the data URI is the prefix &amp;lt;tt&amp;gt;data:text/html,&amp;lt;/tt&amp;gt; plus your HTML.&lt;br /&gt;
&lt;br /&gt;
===Change window resolution===&lt;br /&gt;
[[Image:MoaP-example-size.png‎|thumb|Resized web window]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string message = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message,&lt;br /&gt;
                 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_WIDTH_PIXELS, 128&amp;lt;/span&amp;gt;,&lt;br /&gt;
                 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_HEIGHT_PIXELS, 32&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* The browser will add scroll bars if the rendered HTML doesn&#039;t fit within the specified PRIM_MEDIA_* size.&lt;br /&gt;
&lt;br /&gt;
===Display an image — works with animated GIFs too===&lt;br /&gt;
[[Image:MoaP-example-animgif.gif‎|thumb|Image on a prim]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL =&lt;br /&gt;
           &amp;quot;http://upload.wikimedia.org/wikipedia/commons/7/70/Rotating_earth_(small).gif&amp;quot;;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string dataURI = &amp;quot;data:text/html,&amp;amp;lt;object data=&#039;&amp;quot; + imageURL + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/object&amp;gt;&amp;quot;;&amp;lt;/span&amp;gt;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [PRIM_MEDIA_CURRENT_URL, dataURI,&lt;br /&gt;
              PRIM_MEDIA_WIDTH_PIXELS, 256,&lt;br /&gt;
              PRIM_MEDIA_HEIGHT_PIXELS, 256]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* If attempting to display YouTube video full-frame, see [https://blogs.secondlife.com/community/community/tnt/blog/2010/03/16/viewer-2-tip-shared-media-show-youtube-on-a-full-prims-face this discussion].&lt;br /&gt;
* For images, you can also use &amp;lt;tt&amp;gt;&amp;amp;lt;image src= &amp;gt;&amp;lt;/tt&amp;gt; like this:&lt;br /&gt;
 string dataURI=&amp;quot;data:text/html,&amp;lt;img src=&#039;&amp;quot; + imageURL + &amp;quot;&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
===Display a background image, tiled===&lt;br /&gt;
&lt;br /&gt;
====Method #1 — using CSS in a style element in &amp;lt;head&amp;gt;====&lt;br /&gt;
[[Image:MoaP-example-bgTiled.png‎|thumb|Tiled background image]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL =&lt;br /&gt;
             &amp;quot;http://www.google.com/intl/en_ALL/images/logo.gif&amp;quot;;&lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;amp;lt;head&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;style type=&#039;text/css&#039;&amp;gt;body{background-image:url(\&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;imageURL + &amp;quot;\&amp;quot;);}&amp;amp;lt;/style&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;amp;lt;body&amp;gt;Hello World&amp;amp;lt;/body&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Method #2  — using background attribute in &amp;amp;lt;body&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL = &amp;quot;http://www.google.com/intl/en_ALL/images/logo.gif&amp;quot;;&lt;br /&gt;
         string dataURI = &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;data:text/html,&amp;amp;lt;body background=&#039;&amp;quot; + imageURL + &amp;quot;&#039;&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;Hello World&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Method #3 — using style attribute in &amp;lt;body&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL = &amp;quot;http://www.google.com/intl/en_ALL/images/logo.gif&amp;quot;;&lt;br /&gt;
         string dataURI = &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;data:text/html,&amp;amp;lt;body style=&#039;background-image:url(\&amp;quot;&amp;quot; + imageURL + &amp;quot;\&amp;quot;)&#039;&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;Hello World&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Text colors — using &amp;amp;lt;font&amp;gt; element===&lt;br /&gt;
[[Image:MoaP-example-font-color.png‎|thumb|Font color]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string message = &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;font color=&#039;Red&#039;&amp;gt;Hello World&amp;amp;lt;/font&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 }  &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* You can also specify color and other CSS rules in a style attribute in another element. For example, you can replace message in the example above with:&lt;br /&gt;
&lt;br /&gt;
 string message = &amp;quot;&amp;amp;lt;h2 style=&#039;color:#FF0000&#039;&amp;gt;Hello World&amp;amp;lt;/h2&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
===Link to external CSS file (forms example)===&lt;br /&gt;
[[Image:MoaP-example-externalCSS.png‎|thumb|External CSS file]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string externalCSS =&amp;lt;/span&amp;gt;&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;quot;http://www.google.com/css/modules/buttons/g-button-chocobo.css&amp;quot;;&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;amp;lt;head&amp;gt;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;link href=&#039;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
                 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;externalCSS + &amp;quot;&#039; rel=&#039;stylesheet&#039; type=&#039;text/css&#039; /&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;quot; +&lt;br /&gt;
                 &amp;quot;&amp;amp;lt;form&amp;gt;&amp;amp;lt;input type=&#039;button&#039; value=&#039;Click Me&#039; class=&#039;g-button&#039; /&amp;gt;&amp;amp;lt;/form&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 }  &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* This is one way to work around the 1024-byte limitation of data URIs. The external CSS can be as large as the browser permits. Also see the examples below for putting JavaScript in an external file.&lt;br /&gt;
* The tag &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;lt;/tt&amp;gt; and the attribute &amp;lt;tt&amp;gt;type=&#039;text/css&#039;&amp;lt;/tt&amp;gt; may be omitted to make the data URI a few bytes shorter.&lt;br /&gt;
&lt;br /&gt;
===Calling external JavaScript functions (JQuery example)===&lt;br /&gt;
[[Image:MoaP-example-JQuery.gif‎|thumb|Calling external JQuery]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string externalJavascript =&lt;br /&gt;
     &amp;quot;http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js&amp;quot;;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;amp;lt;head&amp;gt;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;script src=&#039;&amp;quot; + externalJavascript + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;amp;lt;button&amp;gt;Toggle&amp;amp;lt;/button&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;amp;lt;p&amp;gt;Hello&amp;amp;lt;br /&amp;gt;World&amp;amp;lt;/p&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;script&amp;gt;$(&#039;button&#039;).click(function () &amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;{$(&#039;p&#039;).slideToggle(&#039;slow&#039;);});&amp;amp;lt;/script&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* This is a technique for working around the 1024-byte limit for data URIs. When you can&#039;t fit all the JavaScript you need into one data URI, just move some JavaScript functions into an external file and give the browser a data URI that references the external file with a URL. In this example above, the JavaScript for [http://jquery.com/ JQuery] lives in an external file on Google&#039;s servers. You just need enough bytes left over in the data URI to put a JavaScript function call with its parameters inside a &amp;lt;tt&amp;gt;&amp;amp;lt;script&amp;gt;&amp;lt;/tt&amp;gt; element.&lt;br /&gt;
* A data URI can be used as a springboard to generate arbitrary complex web pages by making use of these elements:&lt;br /&gt;
** a reference to external CSS with &amp;lt;tt&amp;gt;&amp;amp;lt;link href= &amp;lt;/tt&amp;gt;, &lt;br /&gt;
** a reference to external JavaScript with &amp;lt;tt&amp;gt;&amp;amp;lt;script src=&amp;lt;/tt&amp;gt;, and&lt;br /&gt;
** a function call with parameters to the external JavaScript using &amp;lt;tt&amp;gt;&amp;amp;lt;script&amp;gt;someFunction(args);&amp;amp;lt;/script&amp;gt;&amp;lt;/tt&amp;gt;. (In the JQuery example above, the name of the external function is &amp;lt;tt&amp;gt;&amp;quot;$&amp;quot;&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* See the next two examples for variations of this same technique.&lt;br /&gt;
&lt;br /&gt;
===Self-served HTML and JavaScript===&lt;br /&gt;
====Example #1 — using &amp;amp;lt;script src= and .innerHTML= ====&lt;br /&gt;
[[Image:MoaP-example-refresh-clock.gif‎|thumb|Self-served auto-refreshed pages]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string myURL;&lt;br /&gt;
 &lt;br /&gt;
 // This can be up to 2KBytes after %-hex-escaping:&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string servedPage =&amp;lt;/span&amp;gt; &amp;quot;&lt;br /&gt;
 function checklength(i){if (i&amp;lt;10) {i=&#039;0&#039;+i;} return i;}      &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;function clock()&amp;lt;/span&amp;gt;{ &lt;br /&gt;
  var now = new Date();  &lt;br /&gt;
  var hours = checklength(now.getHours());  &lt;br /&gt;
  var minutes = checklength(now.getMinutes());  &lt;br /&gt;
  var seconds = checklength(now.getSeconds());  &lt;br /&gt;
  var format = 1; //0=24 hour format, 1=12 hour format   &lt;br /&gt;
  var time;  &lt;br /&gt;
  if (format == 1) { &lt;br /&gt;
   if (hours &amp;gt;= 12) {  &lt;br /&gt;
    if (hours ==12 ) { hours = 12;&lt;br /&gt;
    } else { hours = hours-12; } &lt;br /&gt;
    time=hours+&#039;:&#039;+minutes+&#039;:&#039;+seconds+&#039; PM&#039;;   &lt;br /&gt;
   } else if(hours &amp;lt; 12) { &lt;br /&gt;
    if (hours ==0) {hours=12;}   &lt;br /&gt;
    time=hours+&#039;:&#039;+minutes+&#039;:&#039;+seconds+&#039; AM&#039;;   &lt;br /&gt;
   }   &lt;br /&gt;
  }  &lt;br /&gt;
  if (format == 0) {time= hours+&#039;:&#039;+minutes+&#039;:&#039;+seconds;}   &lt;br /&gt;
  &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;document.getElementById(&#039;clock&#039;).innerHTML=time;&amp;lt;/span&amp;gt;&lt;br /&gt;
  setTimeout(&#039;clock();&#039;, 500); &lt;br /&gt;
 } &amp;quot;; // yes, that&#039;s one long string.&lt;br /&gt;
 &lt;br /&gt;
 displayPage()&lt;br /&gt;
 {&lt;br /&gt;
     string dataURI = &amp;quot;data:text/html,&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;script src=&#039;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;myURL + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;div id=&#039;clock&#039;&amp;gt;&amp;amp;lt;script&amp;gt;clock();&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/div&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
     llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;myURL = body;&amp;lt;/span&amp;gt;&lt;br /&gt;
             displayPage();&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPResponse(id, 200, servedPage);&amp;lt;/span&amp;gt;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* This is a technique for trading the 1024-byte limit of data URIs for the 2048-byte-per-page limit that the script can serve itself. As in the previous example, when you can&#039;t fit all the JavaScript into one data URI, just move the JavaScript into an external file and reference the file with &amp;lt;tt&amp;gt;&amp;amp;lt;script src=URL&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;lt;/tt&amp;gt;. When the URL refers to the script&#039;s own HTTP-in URL, then the page served can contain up to 2K bytes, so you get at least that many bytes plus whatever logic you can fit into the rest of the data URI. If you move that JavaScript to an external web server, the size of the external JavaScript file can be as large as the client web browser can render.&lt;br /&gt;
* Your script can serve an arbitrary number of different pages through one HTTP-in URL by appending a path or parameter to the URL.&lt;br /&gt;
* This works by assigning a string containing HTML to the &amp;lt;tt&amp;gt;.innerHTML&amp;lt;/tt&amp;gt; contents of the &amp;lt;tt&amp;gt;&amp;amp;lt;div&amp;gt;&amp;lt;/tt&amp;gt; element with id &amp;quot;clock&amp;quot;. This achieves something similar to the Ajax-like [http://talirosca.wikidot.com/bootstrapping-html-on-a-prim technique published by Tali Rosca] and mentioned [http://wiki.secondlife.com/wiki/User:Kelly_Linden/lsl_hacks#DHTML_.2F_Javascript_.28Tali_Rosca.29 here]. In those techniques, a string is constructed containing an &amp;lt;tt&amp;gt;&amp;amp;lt;a href=&amp;lt;/tt&amp;gt; tag and an &amp;lt;tt&amp;gt;href=&amp;lt;/tt&amp;gt; that points to the script&#039;s HTTP-in URL. That string then replaces the &amp;lt;tt&amp;gt;&amp;amp;lt;body&amp;gt;&amp;lt;/tt&amp;gt; element using:&lt;br /&gt;
 &amp;lt;tt&amp;gt;document.getElementsByTagName(&#039;body&#039;)[0].innerHTML = &amp;lt;i&amp;gt;new-content&amp;lt;/i&amp;gt;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Example #2 — using &amp;lt;body onload= (lag graph example)====&lt;br /&gt;
[[Image:MoaP-example-laggraph.gif‎|thumb|Refreshed pages]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string jsURL; // where to fetch external JavaScript&lt;br /&gt;
 list numbers;&lt;br /&gt;
 integer numSamples = 50;&lt;br /&gt;
 &lt;br /&gt;
 // This is self-served in this example, but can be&lt;br /&gt;
 // moved to an external server:&lt;br /&gt;
 //&lt;br /&gt;
 string externalJavascript()&lt;br /&gt;
 {&lt;br /&gt;
     return&lt;br /&gt;
         &amp;quot;function bar(widthPct,heightPix) {&amp;quot; +&lt;br /&gt;
         &amp;quot; document.writeln(\&amp;quot;&amp;amp;lt;hr style=&#039;padding:0;margin:0;&amp;quot; +&lt;br /&gt;
         &amp;quot;  margin-top:-1px;text-align:left;align:left;border=0;&amp;quot; +&lt;br /&gt;
         &amp;quot;  width:\&amp;quot;+widthPct+\&amp;quot;%;height:\&amp;quot;+heightPix+\&amp;quot;px;&amp;quot; +&lt;br /&gt;
         &amp;quot;  background-color:#c22;color:#c22;&#039;&amp;gt;\&amp;quot;);}&amp;quot; +&lt;br /&gt;
         &amp;quot; function graphBars(arr){for(var i=0;i&amp;amp;lt;arr.length;++i)&amp;quot; +&lt;br /&gt;
         &amp;quot;  {bar(arr[i],18);}}&amp;quot;;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llClearPrimMedia(face);&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
         integer i = numSamples;&lt;br /&gt;
         while (--i &amp;gt;= 0) {&lt;br /&gt;
             numbers += 0;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     timer()&lt;br /&gt;
     {&lt;br /&gt;
         numbers = llList2List(numbers, 1, -1) + [(integer)(6.0 * (45.0 - llGetRegionFPS()))];&lt;br /&gt;
 &lt;br /&gt;
         // The dataURI loads external JavaScript functions and calls one with parameters:&lt;br /&gt;
 &lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;head&amp;gt;&amp;amp;lt;script src=&#039;&amp;quot; + jsURL + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;body onload=\&amp;quot;graphBars([&amp;quot; + llList2CSV(numbers) + &amp;quot;]);\&amp;quot;&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, dataURI, PRIM_MEDIA_AUTO_PLAY, TRUE]);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             jsURL = body; // self-serve the JavaScript&lt;br /&gt;
             llSetTimerEvent(1.0);&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             llHTTPResponse(id, 200, externalJavascript());&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* Like the previous example, this is another variation of how to structure a data URI to serve as a springboard for arbitrarily large pages. In this example, the data URI first uses a &amp;lt;tt&amp;gt;&amp;amp;lt;script src=...&amp;gt;&amp;lt;/tt&amp;gt; tag to read a file of external JavaScript functions in the &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;lt;/tt&amp;gt; element, and then the &amp;lt;tt&amp;gt;onload=&amp;lt;/tt&amp;gt; attribute in &amp;lt;tt&amp;gt;&amp;amp;lt;body&amp;gt;&amp;lt;/tt&amp;gt; triggers a call to one of the functions which executes a loop creating a bunch of &amp;lt;tt&amp;gt;&amp;amp;lt;hr&amp;gt;&amp;lt;/tt&amp;gt; elements that form the bar chart. The size of HTML generated by the JavaScript can be as large as the browser permits.&lt;br /&gt;
* In this example, &amp;lt;tt&amp;gt;jsURL&amp;lt;/tt&amp;gt; is set to the script&#039;s own HTTP-in URL so that the example can be self-contained, but you can point &amp;lt;tt&amp;gt;jsURL&amp;lt;/tt&amp;gt; to an external URL as well. If served by an external server, the JavaScript read in &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;lt;/tt&amp;gt; can be as large as the browser permits.&lt;br /&gt;
* &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;lt;/tt&amp;gt; can be omitted in the data URI surrounding the &amp;lt;tt&amp;gt;&amp;amp;lt;script src=&amp;lt;/tt&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
===Reverse Ajax - Long-polling the HTTP-in server, chat logger example===&lt;br /&gt;
[[Image:MoaP-example-Long-polling-httpin.gif|thumb|300px|Long-polling HTTP-in]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 // Reverse Ajax: Long-polling HTTP-in.&lt;br /&gt;
 // Becky Pippen, 2010, contributed to the public domain.&lt;br /&gt;
 &lt;br /&gt;
 integer face = 4;          // Prim face for Shared Media&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string  myURL;             // HTTP-in URL&amp;lt;/span&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;key inId = NULL_KEY;       // GET request id&amp;lt;/span&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;list msgQueue = [];        // strings of Javascript&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;// url is our own HTTP-in url.&lt;br /&gt;
 // This sets up a bootloader web page like this:&lt;br /&gt;
 //      &amp;amp;lt;html&amp;gt;&amp;amp;lt;body&amp;gt;&lt;br /&gt;
 //         &amp;amp;lt;div&amp;gt;&amp;amp;lt;script id=&#039;sc&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/div&amp;gt;&lt;br /&gt;
 //         &amp;amp;lt;script&amp;gt; callbacks and poll.beg() defined here &amp;amp;lt;/script&amp;gt;&lt;br /&gt;
 //         &amp;amp;lt;button onclick=poll.beg()&amp;gt;Start&amp;amp;lt;/button&amp;gt;&lt;br /&gt;
 //         &amp;amp;lt;div id=&#039;dv&#039;&amp;gt;&amp;amp;lt;/div&amp;gt;&lt;br /&gt;
 //      &amp;amp;lt;/body&amp;gt;&amp;amp;lt;/html&amp;gt;&lt;br /&gt;
 // When the button is pressed, the JS code sets src= on script#sc&lt;br /&gt;
 // and reattaches the script element to the parent &amp;amp;lt;div&amp;gt; element which&lt;br /&gt;
 // initiates a GET to the prim&#039;s HTTP-in port&lt;br /&gt;
 //&lt;br /&gt;
 setDataURI(string url)&lt;br /&gt;
 {&lt;br /&gt;
     string dataURI = &amp;quot;data:text/html,&lt;br /&gt;
 &amp;amp;lt;!DOCTYPE HTML&amp;gt;&amp;amp;lt;html&amp;gt;&amp;amp;lt;body&amp;gt;&amp;amp;lt;div&amp;gt;&amp;amp;lt;script id=&#039;sc&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/div&amp;gt;&amp;amp;lt;script&amp;gt;&lt;br /&gt;
 var poll=function(){var sc=document.getElementById(&#039;sc&#039;),t2,seq=0,s0;return{&lt;br /&gt;
 beg:function(){s0=document.createElement(&#039;script&#039;);s0.onload=poll.end;t2=setTimeout(&#039;poll.end()&#039;,20000);&lt;br /&gt;
 s0.src=&#039;&amp;quot; + url + &amp;quot;/?r=&#039;+(seq++);sc.parentNode.replaceChild(s0,sc);sc=s0;},&lt;br /&gt;
 end:function(){clearTimeout(t2);t2=null;sc.onload=null;setTimeout(&#039;poll.beg()&#039;,500);},};}();&amp;amp;lt;/script&amp;gt;&lt;br /&gt;
 &amp;amp;lt;button id=&#039;btn&#039;onclick=poll.beg()&amp;gt;Start&amp;amp;lt;/button&amp;gt;&amp;amp;lt;div id=&#039;dv&#039;&amp;gt;&amp;amp;lt;/div&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
     llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, dataURI]);&amp;lt;/span&amp;gt;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;// Returns zero or more queued messages. Assumes no single message is&lt;br /&gt;
 // longer than MAX_SIZE_CHARS (will hang if there is)&lt;br /&gt;
 //&lt;br /&gt;
 string popQueuedMessages()&lt;br /&gt;
 {&lt;br /&gt;
     string  totalMsg = &amp;quot;&amp;quot;;&lt;br /&gt;
     integer totalMsgSize = 0;&lt;br /&gt;
     string  nextMsg = &amp;quot;&amp;quot;;&lt;br /&gt;
     integer nextMsgSize = 0;&lt;br /&gt;
 &lt;br /&gt;
     // HTTP response bodies are limited to 2048 bytes after encoding&lt;br /&gt;
     // in UTF-8. LSL string sizes are measured in characters, which,&lt;br /&gt;
     // in UTF-8, use one byte (for ASCII chars), two bytes (most Latin-1),&lt;br /&gt;
     // or three bytes (a few international characters). So, unless&lt;br /&gt;
     // you re-write this section so that it measures UTF-8 size, keep&lt;br /&gt;
     // MAX_SIZE_CHARS small enough so the text will fit in a response body.&lt;br /&gt;
     //&lt;br /&gt;
     integer MAX_SIZE_CHARS = 1000; // Max HTTP body size &lt;br /&gt;
  &lt;br /&gt;
     integer numMessagesQueued = llGetListLength(msgQueue);&lt;br /&gt;
     integer count = 0;&lt;br /&gt;
     while (count &amp;amp;lt; numMessagesQueued) {&lt;br /&gt;
         nextMsg = llList2String(msgQueue, count);&lt;br /&gt;
         nextMsgSize = llStringLength(nextMsg);&lt;br /&gt;
 &lt;br /&gt;
         if (totalMsgSize + nextMsgSize &amp;amp;lt; MAX_SIZE_CHARS) {&lt;br /&gt;
             totalMsg += nextMsg;&lt;br /&gt;
             totalMsgSize += nextMsgSize;&lt;br /&gt;
             ++count;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     // Delete the messages from the queue that we&#039;re going to send:&lt;br /&gt;
     if (count &amp;gt; 0) {&lt;br /&gt;
         msgQueue = llDeleteSubList(msgQueue, 0, count - 1);&amp;lt;/span&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return totalMsg;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;// Called when there are previous messages still queued, or if there&lt;br /&gt;
 // is no GET request currently open to respond to.&amp;lt;/span&amp;gt;&lt;br /&gt;
 //&lt;br /&gt;
 pushMessageToSend(string msg)&lt;br /&gt;
 {&lt;br /&gt;
     &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;msgQueue = msgQueue + [msg]; // last element is the last one stacked&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
     // See if we can send some messages now:&lt;br /&gt;
     &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;if (inId != NULL_KEY) {&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPResponse(inId, 200, popQueuedMessages());&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;inId = NULL_KEY;&amp;lt;/span&amp;gt;&lt;br /&gt;
     } // else wait for the next incoming GET request&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Replaces all occurrences of &#039;from&#039; with &#039;to&#039; in &#039;src&#039;&lt;br /&gt;
 // From http://snipplr.com/view/13279/lslstrreplace/&lt;br /&gt;
 //&lt;br /&gt;
 string str_replace(string subject, string search, string replace)&lt;br /&gt;
 {&lt;br /&gt;
     return llDumpList2String(llParseStringKeepNulls(subject, [search], []), replace);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Optionally filter out characters in text that would mess up the&lt;br /&gt;
 // web page display. This demo just escapes &#039; and &amp;quot; and adds a space after &#039;&amp;lt;&#039;.&lt;br /&gt;
 //&lt;br /&gt;
 string addSlashes(string s)&lt;br /&gt;
 {&lt;br /&gt;
     return str_replace(str_replace(str_replace(s, &amp;quot;&amp;amp;lt;&amp;quot;, &amp;quot;&amp;amp;lt; &amp;quot;), &amp;quot;\&amp;quot;&amp;quot;, &amp;quot;\\\&amp;quot;&amp;quot;), &amp;quot;&#039;&amp;quot;, &amp;quot;\\\&#039;&amp;quot;);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // This is the main interface for LSL to control the Shared Media web page.&lt;br /&gt;
 // The messages we send consist of Javascript function statements that the&lt;br /&gt;
 // browser will evaluate and execute in the context of the web page.&lt;br /&gt;
 // See sendMessageF() for a similar function with macro replacement.&lt;br /&gt;
 //&lt;br /&gt;
 sendMessage(string msg)&lt;br /&gt;
 {&lt;br /&gt;
     // Test for the easy case: if there are no other messages waiting&lt;br /&gt;
     // in the queue, and if there is an open GET connection, then just&lt;br /&gt;
     // respond immediately:&lt;br /&gt;
  &lt;br /&gt;
     &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;if (llGetListLength(msgQueue) == 0&amp;lt;/span&amp;gt; &amp;amp;&amp;amp; &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;inId != NULL_KEY) {&amp;lt;/span&amp;gt;&lt;br /&gt;
         // Nothing in the queue and an open GET, so respond immediately:&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPResponse(inId, 200, msg);&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;inId = NULL_KEY;&amp;lt;/span&amp;gt;&lt;br /&gt;
     } &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;else {&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;pushMessageToSend(msg);&amp;lt;/span&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Same as sendMessage() but with macro replacements. For each nth string&lt;br /&gt;
 // element in replacements, replace all occurrences of {@n}. For example,&lt;br /&gt;
 //     sendMessageF( &amp;quot;alert(&#039;{@0} {@1}!&#039;)&amp;quot;, [&amp;quot;Hello&amp;quot;, &amp;quot;World&amp;quot;] );&lt;br /&gt;
 // will send &amp;quot;alert(&#039;Hello World!&#039;)&amp;quot; to the web browser.&lt;br /&gt;
 //&lt;br /&gt;
 sendMessageF(string msg, list replacements)&lt;br /&gt;
 {&lt;br /&gt;
     integer numrepl = llGetListLength(replacements);&lt;br /&gt;
     integer i;&lt;br /&gt;
     for (i = 0; i &amp;amp;lt; numrepl; ++i) {&lt;br /&gt;
         msg = str_replace(msg, &amp;quot;{@&amp;quot; + (string)i + &amp;quot;}&amp;quot;, llList2String(replacements, i));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;sendMessage(msg);&amp;lt;/span&amp;gt;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E8E8FF&amp;quot;&amp;gt;// Chat logger demo: writes a new &amp;amp;lt;tr&amp;gt; table row to the web page&lt;br /&gt;
 // for every line of open chat it hears.&lt;br /&gt;
 //&lt;br /&gt;
 webAppInit()&lt;br /&gt;
 {&lt;br /&gt;
     string msg;&lt;br /&gt;
     string m0;&lt;br /&gt;
 &lt;br /&gt;
     // First, send over a few handy function definitions:&lt;br /&gt;
 &lt;br /&gt;
     msg = &amp;quot;function $$(t) { return document.getElementsByTagName(t)[0]; };&amp;quot;;&lt;br /&gt;
     msg += &amp;quot;function h() { return $$(&#039;head&#039;); };&amp;quot;;&lt;br /&gt;
     msg += &amp;quot;function b() { return $$(&#039;body&#039;); };&amp;quot;;&lt;br /&gt;
     msg += &amp;quot;function e(id) { return document.getElementById(id); };&amp;quot;;&lt;br /&gt;
     sendMessage(msg);&lt;br /&gt;
 &lt;br /&gt;
     // Send some CSS. WebKit is sensitive about appending &amp;amp;lt;style&amp;gt; elements&lt;br /&gt;
     // to &amp;amp;lt;head&amp;gt;, so we&#039;ll append it to an existing &amp;amp;lt;div&amp;gt; tag in &amp;amp;lt;body&amp;gt; instead.&lt;br /&gt;
 &lt;br /&gt;
     msg = &amp;quot;e(&#039;dv&#039;).innerHTML += \&amp;quot;{@0}\&amp;quot;;&amp;quot;;&lt;br /&gt;
     m0 = &amp;quot;&amp;amp;lt;style&amp;gt;td:nth-child(2) { text-align:right } tr:nth-child(odd) { background-color:#f8e8f8 }&amp;amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
     sendMessageF(msg, [m0]);&lt;br /&gt;
 &lt;br /&gt;
     // Write a &amp;amp;lt;table&amp;gt; element into element div#dv. The lines of chat will&lt;br /&gt;
     // become rows in this table appended to tbody#tbd&lt;br /&gt;
 &lt;br /&gt;
     msg = &amp;quot;e(&#039;dv&#039;).innerHTML += \&amp;quot;{@0}\&amp;quot;;&amp;quot;;&lt;br /&gt;
     m0 = &amp;quot;&amp;amp;lt;table&amp;gt;&amp;amp;lt;tbody id=&#039;tbd&#039;&amp;gt;&amp;amp;lt;/tbody&amp;gt;&amp;amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
     sendMessageF(msg, [m0]);&lt;br /&gt;
 &lt;br /&gt;
     llListen(0, &amp;quot;&amp;quot;, NULL_KEY, &amp;quot;&amp;quot;);&amp;lt;/span&amp;gt;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llClearPrimMedia(face);&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;        llRequestURL();&amp;lt;/span&amp;gt;&lt;br /&gt;
          &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E8E8FF&amp;quot;&amp;gt;        webAppInit();&amp;lt;/span&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;    http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             myURL = body;&lt;br /&gt;
             llOwnerSay(&amp;quot;myURL=&amp;quot; + myURL);&lt;br /&gt;
             setDataURI(myURL);&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             // Either send some queued messages now with llHTTPResponse(),&lt;br /&gt;
             // or if there&#039;s nothing to do now, save the GET id and&lt;br /&gt;
             // wait for somebody to call sendMessage().&amp;lt;/span&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;            if (llGetListLength(msgQueue) &amp;gt; 0) {&amp;lt;/span&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;                llHTTPResponse(id, 200, popQueuedMessages());&lt;br /&gt;
                 inId = NULL_KEY;&lt;br /&gt;
             } else {&lt;br /&gt;
                 inId = id;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E8E8FF&amp;quot;&amp;gt;    // When we hear chat from name, send a Javascript statement that&lt;br /&gt;
     // appends HTML to element #tbd. I.e., we&#039;ll make a string of HTML&lt;br /&gt;
     // formatted like this:&lt;br /&gt;
     //      &amp;amp;lt;tr style=&amp;quot;color:hsl(200,100%,30%)&amp;quot;&amp;gt;&lt;br /&gt;
     //         &amp;amp;lt;td&amp;gt;[01:23]&amp;amp;lt;/td&amp;gt;&lt;br /&gt;
     //         &amp;amp;lt;td&amp;gt;Avatar Name&amp;amp;lt;/td&amp;gt;&lt;br /&gt;
     //         &amp;amp;lt;td&amp;gt;the chat text&amp;amp;lt;/td&amp;gt;&lt;br /&gt;
     //      &amp;amp;lt;/tr&amp;gt;&lt;br /&gt;
     // and send it wrapped it in a Javascript statement like this:&lt;br /&gt;
     //      e(&#039;tbd&#039;).innerHTML += htmlstring;&lt;br /&gt;
     //&lt;br /&gt;
     listen(integer chan, string name, key id, string chat)&lt;br /&gt;
     {&lt;br /&gt;
         integer s = (integer)(&amp;quot;0x&amp;quot; + llGetSubString((string)id, 0, 6));&lt;br /&gt;
         string hue = (string)(s % 360);&lt;br /&gt;
         string color = &amp;quot;hsl(&amp;quot; + hue + &amp;quot;,100%, 30%)&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         string msg = &amp;quot;e(&#039;tbd&#039;).innerHTML += &#039;{@0}&#039;;&amp;quot;;&lt;br /&gt;
         string m0 = &amp;quot;&amp;amp;lt;tr style=\&amp;quot;color: {@4}\&amp;quot;&amp;gt;&amp;quot;;&lt;br /&gt;
         m0 +=   &amp;quot;&amp;amp;lt;td&amp;gt;{@1}&amp;amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
         m0 +=   &amp;quot;&amp;amp;lt;td&amp;gt;{@2}:&amp;amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
         m0 +=   &amp;quot;&amp;amp;lt;td&amp;gt;{@3}&amp;amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
         m0 += &amp;quot;&amp;amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         string t = llGetSubString(llGetTimestamp(), 11, 15);&lt;br /&gt;
         sendMessageF(msg, [m0, &amp;quot;[&amp;quot; + t + &amp;quot;]&amp;quot;, name, addSlashes(chat), color]);&lt;br /&gt;
     }&amp;lt;/span&amp;gt;&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
&amp;lt;ol start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;This &amp;quot;[http://en.wikipedia.org/wiki/Push_technology server-push]&amp;quot; technique using long-polling gives an LSL script the ability to modify the web page displayed on a prim. The Javascript side always keeps an HTTP GET open to the prim&#039;s HTTP-in URL, and the LSL side responds whenever it wants to with a response consisting of strings of Javascript to be executed by the web browser. For example, this LSL code causes an alert box to pop up on the web page:&lt;br /&gt;
: &amp;lt;code&amp;gt;sendMessage( &amp;quot;alert()&amp;quot; );&amp;lt;/code&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;In the LSL listing above, &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;the yellow highlighting&amp;lt;/span&amp;gt; shows the most important parts of the HTTP long-polling mechanism. The &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;green highlighting&amp;lt;/span&amp;gt; shows the message buffering on top of that. The &amp;lt;span style=&amp;quot;background-color:#E8E8FF&amp;quot;&amp;gt;blue highlighting&amp;lt;/span&amp;gt; is the application code that uses long-polling. To use long-polling for a different application, replace the blue parts. The rest is glue.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;To avoid some subtle WebKit problems when dynamically appending or replacing the first-level child nodes in &amp;amp;lt;head&amp;gt; or &amp;amp;lt;body&amp;gt;, we&#039;ve made two special &amp;amp;lt;div&amp;gt; elements on the bootstrap HTML page. The first surrounds the script#sc element that we replace for each GET. The second is at the end of &amp;amp;lt;body&amp;gt; as a convenient place for the application to insert new content that would otherwise go in &amp;amp;lt;body&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;To make sure that there is nearly always a valid GET request open, the Javascript side starts a new GET after receiving a response, or when the last GET times out.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;There are two timeouts hard-coded in the bootstrap data URI:&lt;br /&gt;
&amp;lt;ol style=&amp;quot;list-style-type:lower-roman&amp;quot;&amp;gt;&amp;lt;li&amp;gt;The &amp;quot;20000&amp;quot; is the timeout (20 seconds) for when an open GET expires, and should be set to something around the minimum of the WebKit outgoing GET request timeout, and the SL-LSL incoming GET timeout. This ensures that GETs are nearly continuous. [[LlHTTPResponse|This page]] says the timeout on the LSL side is 25 seconds.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;The &amp;quot;500&amp;quot; is a throttle that sets an upper limit to how fast the Javascript re-polls HTTP-in to prevent hammering the simulator. It&#039;s a half-second delay after receiving a GET response before starting a new GET.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; If there&#039;s a little gap between two GETs, or if two overlap a little, the sendMessage() message buffering will compensate by queuing up messages to be sent when the next GET arrives. If there are multiple messages in the queue when a GET arrives, the LSL side will concatenate as many messages as possible and send them together.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;On the Javascript side, the GET is not triggered until the &amp;lt;tt&amp;gt;&amp;amp;lt;script&amp;gt;&amp;lt;/tt&amp;gt; element is attached to its parent node with .appendChild() or .replaceChild().&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;The bootstrap Javascript above in setDataURI() had to be compressed somewhat to fit into the 1024-byte data URI limit. Here&#039;s an expanded listing for reference with more descriptive names:&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 &amp;amp;lt;!DOCTYPE HTML&amp;gt;&lt;br /&gt;
 &amp;amp;lt;html&amp;gt;&lt;br /&gt;
   &amp;amp;lt;body&amp;gt;&lt;br /&gt;
     &amp;amp;lt;div&amp;gt;&lt;br /&gt;
       &amp;amp;lt;script id=&#039;script&#039;&amp;gt; &amp;amp;lt;/script&amp;gt;&lt;br /&gt;
     &amp;amp;lt;/div&amp;gt;&lt;br /&gt;
     &amp;amp;lt;script&amp;gt;&lt;br /&gt;
       var poll=function(){&lt;br /&gt;
         var script=document.getElementById(&#039;script&#039;),&lt;br /&gt;
         timeoutId,&lt;br /&gt;
         seq=0,&lt;br /&gt;
         newScript;&lt;br /&gt;
         return {&lt;br /&gt;
           beg:function(){                                      // Initiate a long-poll GET&lt;br /&gt;
             newScript=document.createElement(&#039;script&#039;);        // The response will go here&lt;br /&gt;
             newScript.onload=poll.end;                         // Call poll.end() when we get a response&lt;br /&gt;
             timeoutId=setTimeout(&#039;poll.end()&#039;,20000);          // ... or if we time out&lt;br /&gt;
             newScript.src=&#039; HTTP-in URL goes here /?r=&#039;+(seq++);&lt;br /&gt;
             script.parentNode.replaceChild(newScript,script);  // this triggers the GET&lt;br /&gt;
             script=newScript;},&lt;br /&gt;
 &lt;br /&gt;
           end:function(){&lt;br /&gt;
             clearTimeout(timeoutId);&lt;br /&gt;
             timeoutId=null;&lt;br /&gt;
             script.onload=null;&lt;br /&gt;
             setTimeout(&#039;poll.beg()&#039;,500);                      // Wait a bit before re-polling&lt;br /&gt;
           },&lt;br /&gt;
         };&lt;br /&gt;
       }();&lt;br /&gt;
     &amp;amp;lt;/script&amp;gt;&lt;br /&gt;
     &amp;amp;lt;button id=&#039;btn&#039;onclick=poll.beg()&amp;gt;Start&amp;amp;lt;button&amp;gt;&lt;br /&gt;
     &amp;amp;lt;div id=&#039;dv&#039;&amp;gt; &amp;amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;amp;lt;/body&amp;gt;&lt;br /&gt;
 &amp;amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Make TinyURLs by script===&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 string myTinyURL;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             // Send our full URL to tinyurl.com for conversion&lt;br /&gt;
             // The answer will come back in http_response()&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPRequest(&amp;quot;http://tinyurl.com/api-create.php?url=&amp;quot; + body, [], &amp;quot;&amp;quot;);&amp;lt;/span&amp;gt;&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             llHTTPResponse(id, 200, &amp;quot;Hello Real World from the Virtual World&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_response(key req, integer stat, list met, string body)&lt;br /&gt;
     {&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;myTinyURL = body;&amp;lt;/span&amp;gt;&lt;br /&gt;
         llOwnerSay(&amp;quot;My HTTP-in TinyURL is: &amp;quot; + myTinyURL + &amp;quot; , Click Me!&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* Any URL or data URI can be mapped to a TinyURL. The example above reduces an assigned HTTP-in URL, which is a long one such as:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://sim4605.agni.lindenlab.com:12046/cap/2b9f06f7-431e-5b0f-9271-2d03bd15370b&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
into a TinyURL this size:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://tinyurl.com/y9etul3&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Development hints===&lt;br /&gt;
# You can develop your HTML and JavaScript outside of Second Life by putting your JavaScript incantations in data URIs that you feed to any web browser. For the most compatible browsers, use Safari or Google Chrome, or any other that uses the [[QtWebKit|WebKit]] HTML rendering engine. If you can&#039;t run Safari or Chrome, try the open source [http://code.google.com/p/arora/ Arora web browser] for simulating Media-on-a-Prim: it&#039;s based on straight-up WebKit and has the same handy inspector/debugger found in other WebKit browsers.&lt;br /&gt;
# If you don&#039;t have direct access to a web server while developing this stuff, then consider installing a local copy of [http://www.apache.org/ Apache web server] on your computer. You can restrict it to local access. Then you can refer to files on your own computer with URLs that start with &amp;lt;tt&amp;gt;&amp;lt;nowiki&amp;gt;http://localhost/&amp;lt;/nowiki&amp;gt;...&amp;lt;/tt&amp;gt;, and PHP scripts can simulate what the HTTP-in server would serve. That lets you, for example, edit &amp;lt;tt&amp;gt;myfile.js&amp;lt;/tt&amp;gt; on your own computer while working on a data URI that contains &amp;lt;tt&amp;gt;&amp;amp;lt;script src=&amp;quot;&amp;lt;nowiki&amp;gt;http://localhost/myfile.js&amp;lt;/nowiki&amp;gt;&amp;quot;&amp;gt;&amp;lt;/tt&amp;gt;. Then when &amp;lt;tt&amp;gt;myfile.js&amp;lt;/tt&amp;gt; is working, you can put the debugged code into an LSL script that will serve it through its HTTP-in port. That&#039;s easier than debugging all that in-world.&lt;br /&gt;
&lt;br /&gt;
===References and sources===&lt;br /&gt;
* [[User:Kelly_Linden/lsl_hacks|Kelly Linden&#039;s hacks]]&lt;br /&gt;
* [http://talirosca.wikidot.com/bootstrapping-html-on-a-prim Tali Rosca&#039;s techniques]&lt;br /&gt;
* [https://blogs.secondlife.com/message/115303 Notecard Text on a Prim - Demo]&lt;br /&gt;
* [[llSetPrimMediaParams]]()&lt;br /&gt;
* {{JIRA|SVC-3427}}&lt;br /&gt;
* [https://blogs.secondlife.com/message/124144 Ajax in Shared Media #1]&lt;br /&gt;
* [https://blogs.secondlife.com/thread/11698 Ajax in Shared Media #2]&lt;br /&gt;
* [https://blogs.secondlife.com/thread/12727 Ajax in Shared Media #3]&lt;br /&gt;
* [https://blogs.secondlife.com/thread/13455 SVG (Scalable Vector Graphics) in Shared Media]&lt;br /&gt;
* [[Shared_Media_and_data_URI|Shared_Media_and_data_URI]]&lt;br /&gt;
* [https://blogs.secondlife.com/community/community/tnt/blog/2010/03/17/viewer-2-tip-shared-media-add-custom-content-with-data-uri-no-webpage-upload-needed Shared Media: Add custom content with data: URI]&lt;br /&gt;
* [https://blogs.secondlife.com/message/119704 &amp;quot;Shared Media&amp;quot; - A few facts]&lt;br /&gt;
&lt;br /&gt;
[[Category:Shared Media]]&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Shared_Media_LSL_Recipes&amp;diff=875983</id>
		<title>User:Becky Pippen/Shared Media LSL Recipes</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Shared_Media_LSL_Recipes&amp;diff=875983"/>
		<updated>2010-04-23T06:20:20Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==How-to&#039;s with Media-on-a-Prim==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
  MediaWiki issue: If anyone knows how to highlight parts of code inside the &amp;lt;lsl&amp;gt;&lt;br /&gt;
  wrapper, let me know. I resorted to using a style attribute in a &amp;lt;span&amp;gt; element&lt;br /&gt;
  inside a &amp;lt;tt&amp;gt; element to yellow-highlight preformatted monospace text. It would&lt;br /&gt;
  be nicer if we could also get the LSL syntax highlighting, but if we have to choose&lt;br /&gt;
  one or the other, I think the yellow highlighting is more useful and instructive&lt;br /&gt;
  for this particular page than LSL syntax highlighting.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The examples here assume you&#039;re comfortable with LSL, and you just need a quick reference for the syntax of handling HTML and JavaScript in LSL for Shared Media scripting. All the examples on this page are ready to copy-n-paste and drop into a prim — no other setup needed. The key syntax in each example is highlighted. There&#039;s no error handling in these minimalist examples, so add plenty of your own.&lt;br /&gt;
&lt;br /&gt;
===Display plain text — XyText replacement===&lt;br /&gt;
[[Image:MoaP-example_1.png‎|thumb|Plain text]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;integer face = 4;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string message = &amp;quot;Hello World&amp;quot;;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llSetPrimMediaParams(face,&amp;lt;/span&amp;gt;&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;[PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message]);&amp;lt;/span&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* An avatar must press the Reload button on the browser to see this rendered in-world. A page will be auto-loaded if you include the parameter &amp;lt;tt&amp;gt;PRIM_MEDIA_AUTO_PLAY, TRUE&amp;lt;/tt&amp;gt;, and if the avatar has auto-load enabled in preferences.&lt;br /&gt;
* There&#039;s no need to add any %-hex-escaping or to use [[llEscapeURL]](); llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, url]) will automatically escape the string for you.&lt;br /&gt;
* This works by feeding a data URI to the on-prim browser. Think of it as serving the contents of the page in the URL itself. For more information, see [[Shared_Media_and_data_URI|here]] and [http://en.wikipedia.org/wiki/Data_URI_scheme here].&lt;br /&gt;
* The in-world browser limits the data URI (or any address) to 1024 bytes after %-hex-escaping and encoded in UTF-8. See below for tricks using external or self-served JavaScript or CSS to leverage the data URI.&lt;br /&gt;
* If the data URI contains just plain text, the prefix &amp;lt;tt&amp;gt;&amp;quot;data:text/html,&amp;quot;&amp;lt;/tt&amp;gt; can be abbreviated &amp;lt;tt&amp;gt;&amp;quot;data:,&amp;quot;&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Force a page reload===&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
[[Image:MoaP-example-refreshpage.gif|thumb|Auto-refresh]]&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string myURL;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;integer seq = 0; // sequence number for unique URLs&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             myURL = body;&lt;br /&gt;
             llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_AUTO_PLAY, TRUE,&lt;br /&gt;
                  PRIM_MEDIA_CURRENT_URL, myURL]);&lt;br /&gt;
             llSetTimerEvent(5.0);&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             llHTTPResponse(id, 200, &amp;quot;Sim FPS: &amp;quot; + (string)llGetRegionFPS());&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     timer()&lt;br /&gt;
     {&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_CURRENT_URL, myURL + &amp;quot;/?r=&amp;quot; + (string)(++seq)&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
Comments:&lt;br /&gt;
* The page will reload automatically if &amp;lt;tt&amp;gt;PRIM_MEDIA_AUTO_PLAY&amp;lt;/tt&amp;gt; is enabled and if the new &amp;lt;tt&amp;gt;PRIM_MEDIA_CURRENT_URL&amp;lt;/tt&amp;gt; is different. This technique appends a short dummy parameter (like &amp;lt;tt&amp;gt;&amp;quot;/?r=12&amp;quot;&amp;lt;/tt&amp;gt;) to the end of the URL to make it different each time, forcing a reload. The parameter is otherwise ignored.&lt;br /&gt;
&lt;br /&gt;
===Display text with HTML markup===&lt;br /&gt;
[[Image:MoaP-example-markup.png‎‎|thumb|HTML markup]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string message = &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;quot;&amp;amp;lt;i&amp;gt;Hello&amp;amp;lt;/i&amp;gt;&amp;amp;lt;h2&amp;gt;World!&amp;amp;lt;/h2&amp;gt;&amp;quot;&amp;lt;/span&amp;gt;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* The text of the data URI will be %-hex-escaped automatically for you, so there&#039;s no need to use [[llEscapeURL]](). All you need in the data URI is the prefix &amp;lt;tt&amp;gt;data:text/html,&amp;lt;/tt&amp;gt; plus your HTML.&lt;br /&gt;
&lt;br /&gt;
===Change window resolution===&lt;br /&gt;
[[Image:MoaP-example-size.png‎|thumb|Resized web window]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string message = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message,&lt;br /&gt;
                 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_WIDTH_PIXELS, 128&amp;lt;/span&amp;gt;,&lt;br /&gt;
                 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_HEIGHT_PIXELS, 32&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* The browser will add scroll bars if the rendered HTML doesn&#039;t fit within the specified PRIM_MEDIA_* size.&lt;br /&gt;
&lt;br /&gt;
===Display an image — works with animated GIFs too===&lt;br /&gt;
[[Image:MoaP-example-animgif.gif‎|thumb|Image on a prim]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL =&lt;br /&gt;
           &amp;quot;http://upload.wikimedia.org/wikipedia/commons/7/70/Rotating_earth_(small).gif&amp;quot;;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string dataURI = &amp;quot;data:text/html,&amp;amp;lt;object data=&#039;&amp;quot; + imageURL + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/object&amp;gt;&amp;quot;;&amp;lt;/span&amp;gt;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [PRIM_MEDIA_CURRENT_URL, dataURI,&lt;br /&gt;
              PRIM_MEDIA_WIDTH_PIXELS, 256,&lt;br /&gt;
              PRIM_MEDIA_HEIGHT_PIXELS, 256]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* If attempting to display YouTube video full-frame, see [https://blogs.secondlife.com/community/community/tnt/blog/2010/03/16/viewer-2-tip-shared-media-show-youtube-on-a-full-prims-face this discussion].&lt;br /&gt;
* For images, you can also use &amp;lt;tt&amp;gt;&amp;amp;lt;image src= &amp;gt;&amp;lt;/tt&amp;gt; like this:&lt;br /&gt;
 string dataURI=&amp;quot;data:text/html,&amp;lt;img src=&#039;&amp;quot; + imageURL + &amp;quot;&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
===Display a background image, tiled===&lt;br /&gt;
&lt;br /&gt;
====Method #1 — using CSS in a style element in &amp;lt;head&amp;gt;====&lt;br /&gt;
[[Image:MoaP-example-bgTiled.png‎|thumb|Tiled background image]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL =&lt;br /&gt;
             &amp;quot;http://www.google.com/intl/en_ALL/images/logo.gif&amp;quot;;&lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;amp;lt;head&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;style type=&#039;text/css&#039;&amp;gt;body{background-image:url(\&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;imageURL + &amp;quot;\&amp;quot;);}&amp;amp;lt;/style&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;amp;lt;body&amp;gt;Hello World&amp;amp;lt;/body&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Method #2  — using background attribute in &amp;amp;lt;body&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL = &amp;quot;http://www.google.com/intl/en_ALL/images/logo.gif&amp;quot;;&lt;br /&gt;
         string dataURI = &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;data:text/html,&amp;amp;lt;body background=&#039;&amp;quot; + imageURL + &amp;quot;&#039;&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;Hello World&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Method #3 — using style attribute in &amp;lt;body&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL = &amp;quot;http://www.google.com/intl/en_ALL/images/logo.gif&amp;quot;;&lt;br /&gt;
         string dataURI = &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;data:text/html,&amp;amp;lt;body style=&#039;background-image:url(\&amp;quot;&amp;quot; + imageURL + &amp;quot;\&amp;quot;)&#039;&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;Hello World&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Text colors — using &amp;amp;lt;font&amp;gt; element===&lt;br /&gt;
[[Image:MoaP-example-font-color.png‎|thumb|Font color]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string message = &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;font color=&#039;Red&#039;&amp;gt;Hello World&amp;amp;lt;/font&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 }  &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* You can also specify color and other CSS rules in a style attribute in another element. For example, you can replace message in the example above with:&lt;br /&gt;
&lt;br /&gt;
 string message = &amp;quot;&amp;amp;lt;h2 style=&#039;color:#FF0000&#039;&amp;gt;Hello World&amp;amp;lt;/h2&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
===Link to external CSS file (forms example)===&lt;br /&gt;
[[Image:MoaP-example-externalCSS.png‎|thumb|External CSS file]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string externalCSS =&amp;lt;/span&amp;gt;&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;quot;http://www.google.com/css/modules/buttons/g-button-chocobo.css&amp;quot;;&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;amp;lt;head&amp;gt;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;link href=&#039;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
                 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;externalCSS + &amp;quot;&#039; rel=&#039;stylesheet&#039; type=&#039;text/css&#039; /&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;quot; +&lt;br /&gt;
                 &amp;quot;&amp;amp;lt;form&amp;gt;&amp;amp;lt;input type=&#039;button&#039; value=&#039;Click Me&#039; class=&#039;g-button&#039; /&amp;gt;&amp;amp;lt;/form&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 }  &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* This is one way to work around the 1024-byte limitation of data URIs. The external CSS can be as large as the browser permits. Also see the examples below for putting JavaScript in an external file.&lt;br /&gt;
* The tag &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;lt;/tt&amp;gt; and the attribute &amp;lt;tt&amp;gt;type=&#039;text/css&#039;&amp;lt;/tt&amp;gt; may be omitted to make the data URI a few bytes shorter.&lt;br /&gt;
&lt;br /&gt;
===Calling external JavaScript functions (JQuery example)===&lt;br /&gt;
[[Image:MoaP-example-JQuery.gif‎|thumb|Calling external JQuery]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string externalJavascript =&lt;br /&gt;
     &amp;quot;http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js&amp;quot;;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;amp;lt;head&amp;gt;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;script src=&#039;&amp;quot; + externalJavascript + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;amp;lt;button&amp;gt;Toggle&amp;amp;lt;/button&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;amp;lt;p&amp;gt;Hello&amp;amp;lt;br /&amp;gt;World&amp;amp;lt;/p&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;script&amp;gt;$(&#039;button&#039;).click(function () &amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;{$(&#039;p&#039;).slideToggle(&#039;slow&#039;);});&amp;amp;lt;/script&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* This is a technique for working around the 1024-byte limit for data URIs. When you can&#039;t fit all the JavaScript you need into one data URI, just move some JavaScript functions into an external file and give the browser a data URI that references the external file with a URL. In this example above, the JavaScript for [http://jquery.com/ JQuery] lives in an external file on Google&#039;s servers. You just need enough bytes left over in the data URI to put a JavaScript function call with its parameters inside a &amp;lt;tt&amp;gt;&amp;amp;lt;script&amp;gt;&amp;lt;/tt&amp;gt; element.&lt;br /&gt;
* A data URI can be used as a springboard to generate arbitrary complex web pages by making use of these elements:&lt;br /&gt;
** a reference to external CSS with &amp;lt;tt&amp;gt;&amp;amp;lt;link href= &amp;lt;/tt&amp;gt;, &lt;br /&gt;
** a reference to external JavaScript with &amp;lt;tt&amp;gt;&amp;amp;lt;script src=&amp;lt;/tt&amp;gt;, and&lt;br /&gt;
** a function call with parameters to the external JavaScript using &amp;lt;tt&amp;gt;&amp;amp;lt;script&amp;gt;someFunction(args);&amp;amp;lt;/script&amp;gt;&amp;lt;/tt&amp;gt;. (In the JQuery example above, the name of the external function is &amp;lt;tt&amp;gt;&amp;quot;$&amp;quot;&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* See the next two examples for variations of this same technique.&lt;br /&gt;
&lt;br /&gt;
===Self-served HTML and JavaScript===&lt;br /&gt;
====Example #1 — using &amp;amp;lt;script src= and .innerHTML= ====&lt;br /&gt;
[[Image:MoaP-example-refresh-clock.gif‎|thumb|Self-served auto-refreshed pages]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string myURL;&lt;br /&gt;
 &lt;br /&gt;
 // This can be up to 2KBytes after %-hex-escaping:&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string servedPage =&amp;lt;/span&amp;gt; &amp;quot;&lt;br /&gt;
 function checklength(i){if (i&amp;lt;10) {i=&#039;0&#039;+i;} return i;}      &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;function clock()&amp;lt;/span&amp;gt;{ &lt;br /&gt;
  var now = new Date();  &lt;br /&gt;
  var hours = checklength(now.getHours());  &lt;br /&gt;
  var minutes = checklength(now.getMinutes());  &lt;br /&gt;
  var seconds = checklength(now.getSeconds());  &lt;br /&gt;
  var format = 1; //0=24 hour format, 1=12 hour format   &lt;br /&gt;
  var time;  &lt;br /&gt;
  if (format == 1) { &lt;br /&gt;
   if (hours &amp;gt;= 12) {  &lt;br /&gt;
    if (hours ==12 ) { hours = 12;&lt;br /&gt;
    } else { hours = hours-12; } &lt;br /&gt;
    time=hours+&#039;:&#039;+minutes+&#039;:&#039;+seconds+&#039; PM&#039;;   &lt;br /&gt;
   } else if(hours &amp;lt; 12) { &lt;br /&gt;
    if (hours ==0) {hours=12;}   &lt;br /&gt;
    time=hours+&#039;:&#039;+minutes+&#039;:&#039;+seconds+&#039; AM&#039;;   &lt;br /&gt;
   }   &lt;br /&gt;
  }  &lt;br /&gt;
  if (format == 0) {time= hours+&#039;:&#039;+minutes+&#039;:&#039;+seconds;}   &lt;br /&gt;
  &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;document.getElementById(&#039;clock&#039;).innerHTML=time;&amp;lt;/span&amp;gt;&lt;br /&gt;
  setTimeout(&#039;clock();&#039;, 500); &lt;br /&gt;
 } &amp;quot;; // yes, that&#039;s one long string.&lt;br /&gt;
 &lt;br /&gt;
 displayPage()&lt;br /&gt;
 {&lt;br /&gt;
     string dataURI = &amp;quot;data:text/html,&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;script src=&#039;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;myURL + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;div id=&#039;clock&#039;&amp;gt;&amp;amp;lt;script&amp;gt;clock();&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/div&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
     llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;myURL = body;&amp;lt;/span&amp;gt;&lt;br /&gt;
             displayPage();&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPResponse(id, 200, servedPage);&amp;lt;/span&amp;gt;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* This is a technique for trading the 1024-byte limit of data URIs for the 2048-byte-per-page limit that the script can serve itself. As in the previous example, when you can&#039;t fit all the JavaScript into one data URI, just move the JavaScript into an external file and reference the file with &amp;lt;tt&amp;gt;&amp;amp;lt;script src=URL&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;lt;/tt&amp;gt;. When the URL refers to the script&#039;s own HTTP-in URL, then the page served can contain up to 2K bytes, so you get at least that many bytes plus whatever logic you can fit into the rest of the data URI. If you move that JavaScript to an external web server, the size of the external JavaScript file can be as large as the client web browser can render.&lt;br /&gt;
* Your script can serve an arbitrary number of different pages through one HTTP-in URL by appending a path or parameter to the URL.&lt;br /&gt;
* This works by assigning a string containing HTML to the &amp;lt;tt&amp;gt;.innerHTML&amp;lt;/tt&amp;gt; contents of the &amp;lt;tt&amp;gt;&amp;amp;lt;div&amp;gt;&amp;lt;/tt&amp;gt; element with id &amp;quot;clock&amp;quot;. This achieves something similar to the Ajax-like [http://talirosca.wikidot.com/bootstrapping-html-on-a-prim technique published by Tali Rosca] and mentioned [http://wiki.secondlife.com/wiki/User:Kelly_Linden/lsl_hacks#DHTML_.2F_Javascript_.28Tali_Rosca.29 here]. In those techniques, a string is constructed containing an &amp;lt;tt&amp;gt;&amp;amp;lt;a href=&amp;lt;/tt&amp;gt; tag and an &amp;lt;tt&amp;gt;href=&amp;lt;/tt&amp;gt; that points to the script&#039;s HTTP-in URL. That string then replaces the &amp;lt;tt&amp;gt;&amp;amp;lt;body&amp;gt;&amp;lt;/tt&amp;gt; element using:&lt;br /&gt;
 &amp;lt;tt&amp;gt;document.getElementsByTagName(&#039;body&#039;)[0].innerHTML = &amp;lt;i&amp;gt;new-content&amp;lt;/i&amp;gt;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Example #2 — using &amp;lt;body onload= (lag graph example)====&lt;br /&gt;
[[Image:MoaP-example-laggraph.gif‎|thumb|Refreshed pages]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string jsURL; // where to fetch external JavaScript&lt;br /&gt;
 list numbers;&lt;br /&gt;
 integer numSamples = 50;&lt;br /&gt;
 &lt;br /&gt;
 // This is self-served in this example, but can be&lt;br /&gt;
 // moved to an external server:&lt;br /&gt;
 //&lt;br /&gt;
 string externalJavascript()&lt;br /&gt;
 {&lt;br /&gt;
     return&lt;br /&gt;
         &amp;quot;function bar(widthPct,heightPix) {&amp;quot; +&lt;br /&gt;
         &amp;quot; document.writeln(\&amp;quot;&amp;amp;lt;hr style=&#039;padding:0;margin:0;&amp;quot; +&lt;br /&gt;
         &amp;quot;  margin-top:-1px;text-align:left;align:left;border=0;&amp;quot; +&lt;br /&gt;
         &amp;quot;  width:\&amp;quot;+widthPct+\&amp;quot;%;height:\&amp;quot;+heightPix+\&amp;quot;px;&amp;quot; +&lt;br /&gt;
         &amp;quot;  background-color:#c22;color:#c22;&#039;&amp;gt;\&amp;quot;);}&amp;quot; +&lt;br /&gt;
         &amp;quot; function graphBars(arr){for(var i=0;i&amp;amp;lt;arr.length;++i)&amp;quot; +&lt;br /&gt;
         &amp;quot;  {bar(arr[i],18);}}&amp;quot;;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llClearPrimMedia(face);&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
         integer i = numSamples;&lt;br /&gt;
         while (--i &amp;gt;= 0) {&lt;br /&gt;
             numbers += 0;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     timer()&lt;br /&gt;
     {&lt;br /&gt;
         numbers = llList2List(numbers, 1, -1) + [(integer)(6.0 * (45.0 - llGetRegionFPS()))];&lt;br /&gt;
 &lt;br /&gt;
         // The dataURI loads external JavaScript functions and calls one with parameters:&lt;br /&gt;
 &lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;head&amp;gt;&amp;amp;lt;script src=&#039;&amp;quot; + jsURL + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;body onload=\&amp;quot;graphBars([&amp;quot; + llList2CSV(numbers) + &amp;quot;]);\&amp;quot;&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, dataURI, PRIM_MEDIA_AUTO_PLAY, TRUE]);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             jsURL = body; // self-serve the JavaScript&lt;br /&gt;
             llSetTimerEvent(1.0);&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             llHTTPResponse(id, 200, externalJavascript());&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* Like the previous example, this is another variation of how to structure a data URI to serve as a springboard for arbitrarily large pages. In this example, the data URI first uses a &amp;lt;tt&amp;gt;&amp;amp;lt;script src=...&amp;gt;&amp;lt;/tt&amp;gt; tag to read a file of external JavaScript functions in the &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;lt;/tt&amp;gt; element, and then the &amp;lt;tt&amp;gt;onload=&amp;lt;/tt&amp;gt; attribute in &amp;lt;tt&amp;gt;&amp;amp;lt;body&amp;gt;&amp;lt;/tt&amp;gt; triggers a call to one of the functions which executes a loop creating a bunch of &amp;lt;tt&amp;gt;&amp;amp;lt;hr&amp;gt;&amp;lt;/tt&amp;gt; elements that form the bar chart. The size of HTML generated by the JavaScript can be as large as the browser permits.&lt;br /&gt;
* In this example, &amp;lt;tt&amp;gt;jsURL&amp;lt;/tt&amp;gt; is set to the script&#039;s own HTTP-in URL so that the example can be self-contained, but you can point &amp;lt;tt&amp;gt;jsURL&amp;lt;/tt&amp;gt; to an external URL as well. If served by an external server, the JavaScript read in &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;lt;/tt&amp;gt; can be as large as the browser permits.&lt;br /&gt;
* &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;lt;/tt&amp;gt; can be omitted in the data URI surrounding the &amp;lt;tt&amp;gt;&amp;amp;lt;script src=&amp;lt;/tt&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
===Reverse Ajax — Long-polling the HTTP-in server (chat logger example)===&lt;br /&gt;
[[Image:MoaP-example-Long-polling-httpin.gif|thumb|300px|Long-polling HTTP-in]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 // Reverse Ajax: Long-polling HTTP-in.&lt;br /&gt;
 // Becky Pippen, 2010, contributed to the public domain.&lt;br /&gt;
 &lt;br /&gt;
 integer face = 4;          // Prim face for Shared Media&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string  myURL;             // HTTP-in URL&amp;lt;/span&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;key inId = NULL_KEY;       // GET request id&amp;lt;/span&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;list msgQueue = [];        // strings of Javascript&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;// url is our own HTTP-in url.&lt;br /&gt;
 // This sets up a bootloader web page like this:&lt;br /&gt;
 //      &amp;amp;lt;html&amp;gt;&amp;amp;lt;body&amp;gt;&lt;br /&gt;
 //         &amp;amp;lt;div&amp;gt;&amp;amp;lt;script id=&#039;sc&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/div&amp;gt;&lt;br /&gt;
 //         &amp;amp;lt;script&amp;gt; callbacks and poll.beg() defined here &amp;amp;lt;/script&amp;gt;&lt;br /&gt;
 //         &amp;amp;lt;button onclick=poll.beg()&amp;gt;Start&amp;amp;lt;/button&amp;gt;&lt;br /&gt;
 //         &amp;amp;lt;div id=&#039;dv&#039;&amp;gt;&amp;amp;lt;/div&amp;gt;&lt;br /&gt;
 //      &amp;amp;lt;/body&amp;gt;&amp;amp;lt;/html&amp;gt;&lt;br /&gt;
 // When the button is pressed, the JS code sets src= on script#sc&lt;br /&gt;
 // and reattaches the script element to the parent &amp;amp;lt;div&amp;gt; element which&lt;br /&gt;
 // initiates a GET to the prim&#039;s HTTP-in port&lt;br /&gt;
 //&lt;br /&gt;
 setDataURI(string url)&lt;br /&gt;
 {&lt;br /&gt;
     string dataURI = &amp;quot;data:text/html,&lt;br /&gt;
 &amp;amp;lt;!DOCTYPE HTML&amp;gt;&amp;amp;lt;html&amp;gt;&amp;amp;lt;body&amp;gt;&amp;amp;lt;div&amp;gt;&amp;amp;lt;script id=&#039;sc&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/div&amp;gt;&amp;amp;lt;script&amp;gt;&lt;br /&gt;
 var poll=function(){var sc=document.getElementById(&#039;sc&#039;),t2,seq=0,s0;return{&lt;br /&gt;
 beg:function(){s0=document.createElement(&#039;script&#039;);s0.onload=poll.end;t2=setTimeout(&#039;poll.end()&#039;,20000);&lt;br /&gt;
 s0.src=&#039;&amp;quot; + url + &amp;quot;/?r=&#039;+(seq++);sc.parentNode.replaceChild(s0,sc);sc=s0;},&lt;br /&gt;
 end:function(){clearTimeout(t2);t2=null;sc.onload=null;setTimeout(&#039;poll.beg()&#039;,500);},};}();&amp;amp;lt;/script&amp;gt;&lt;br /&gt;
 &amp;amp;lt;button id=&#039;btn&#039;onclick=poll.beg()&amp;gt;Start&amp;amp;lt;/button&amp;gt;&amp;amp;lt;div id=&#039;dv&#039;&amp;gt;&amp;amp;lt;/div&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
     llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, dataURI]);&amp;lt;/span&amp;gt;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;// Returns zero or more queued messages. Assumes no single message is&lt;br /&gt;
 // longer than MAX_SIZE_CHARS (will hang if there is)&lt;br /&gt;
 //&lt;br /&gt;
 string popQueuedMessages()&lt;br /&gt;
 {&lt;br /&gt;
     string  totalMsg = &amp;quot;&amp;quot;;&lt;br /&gt;
     integer totalMsgSize = 0;&lt;br /&gt;
     string  nextMsg = &amp;quot;&amp;quot;;&lt;br /&gt;
     integer nextMsgSize = 0;&lt;br /&gt;
 &lt;br /&gt;
     // HTTP response bodies are limited to 2048 bytes after encoding&lt;br /&gt;
     // in UTF-8. LSL string sizes are measured in characters, which,&lt;br /&gt;
     // in UTF-8, use one byte (for ASCII chars), two bytes (most Latin-1),&lt;br /&gt;
     // or three bytes (a few international characters). So, unless&lt;br /&gt;
     // you re-write this section so that it measures UTF-8 size, keep&lt;br /&gt;
     // MAX_SIZE_CHARS small enough so the text will fit in a response body.&lt;br /&gt;
     //&lt;br /&gt;
     integer MAX_SIZE_CHARS = 1000; // Max HTTP body size &lt;br /&gt;
  &lt;br /&gt;
     integer numMessagesQueued = llGetListLength(msgQueue);&lt;br /&gt;
     integer count = 0;&lt;br /&gt;
     while (count &amp;amp;lt; numMessagesQueued) {&lt;br /&gt;
         nextMsg = llList2String(msgQueue, count);&lt;br /&gt;
         nextMsgSize = llStringLength(nextMsg);&lt;br /&gt;
 &lt;br /&gt;
         if (totalMsgSize + nextMsgSize &amp;amp;lt; MAX_SIZE_CHARS) {&lt;br /&gt;
             totalMsg += nextMsg;&lt;br /&gt;
             totalMsgSize += nextMsgSize;&lt;br /&gt;
             ++count;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     // Delete the messages from the queue that we&#039;re going to send:&lt;br /&gt;
     if (count &amp;gt; 0) {&lt;br /&gt;
         msgQueue = llDeleteSubList(msgQueue, 0, count - 1);&amp;lt;/span&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return totalMsg;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;// Called when there are previous messages still queued, or if there&lt;br /&gt;
 // is no GET request currently open to respond to.&amp;lt;/span&amp;gt;&lt;br /&gt;
 //&lt;br /&gt;
 pushMessageToSend(string msg)&lt;br /&gt;
 {&lt;br /&gt;
     &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;msgQueue = msgQueue + [msg]; // last element is the last one stacked&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
     // See if we can send some messages now:&lt;br /&gt;
     &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;if (inId != NULL_KEY) {&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPResponse(inId, 200, popQueuedMessages());&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;inId = NULL_KEY;&amp;lt;/span&amp;gt;&lt;br /&gt;
     } // else wait for the next incoming GET request&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Replaces all occurrences of &#039;from&#039; with &#039;to&#039; in &#039;src&#039;&lt;br /&gt;
 // From http://snipplr.com/view/13279/lslstrreplace/&lt;br /&gt;
 //&lt;br /&gt;
 string str_replace(string subject, string search, string replace)&lt;br /&gt;
 {&lt;br /&gt;
     return llDumpList2String(llParseStringKeepNulls(subject, [search], []), replace);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Optionally filter out characters in text that would mess up the&lt;br /&gt;
 // web page display. This demo just escapes &#039; and &amp;quot; and adds a space after &#039;&amp;lt;&#039;.&lt;br /&gt;
 //&lt;br /&gt;
 string addSlashes(string s)&lt;br /&gt;
 {&lt;br /&gt;
     return str_replace(str_replace(str_replace(s, &amp;quot;&amp;amp;lt;&amp;quot;, &amp;quot;&amp;amp;lt; &amp;quot;), &amp;quot;\&amp;quot;&amp;quot;, &amp;quot;\\\&amp;quot;&amp;quot;), &amp;quot;&#039;&amp;quot;, &amp;quot;\\\&#039;&amp;quot;);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // This is the main interface for LSL to control the Shared Media web page.&lt;br /&gt;
 // The messages we send consist of Javascript function statements that the&lt;br /&gt;
 // browser will evaluate and execute in the context of the web page.&lt;br /&gt;
 // See sendMessageF() for a similar function with macro replacement.&lt;br /&gt;
 //&lt;br /&gt;
 sendMessage(string msg)&lt;br /&gt;
 {&lt;br /&gt;
     // Test for the easy case: if there are no other messages waiting&lt;br /&gt;
     // in the queue, and if there is an open GET connection, then just&lt;br /&gt;
     // respond immediately:&lt;br /&gt;
  &lt;br /&gt;
     &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;if (llGetListLength(msgQueue) == 0&amp;lt;/span&amp;gt; &amp;amp;&amp;amp; &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;inId != NULL_KEY) {&amp;lt;/span&amp;gt;&lt;br /&gt;
         // Nothing in the queue and an open GET, so respond immediately:&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPResponse(inId, 200, msg);&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;inId = NULL_KEY;&amp;lt;/span&amp;gt;&lt;br /&gt;
     } &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;else {&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;pushMessageToSend(msg);&amp;lt;/span&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Same as sendMessage() but with macro replacements. For each nth string&lt;br /&gt;
 // element in replacements, replace all occurrences of {@n}. For example,&lt;br /&gt;
 //     sendMessageF( &amp;quot;alert(&#039;{@0} {@1}!&#039;)&amp;quot;, [&amp;quot;Hello&amp;quot;, &amp;quot;World&amp;quot;] );&lt;br /&gt;
 // will send &amp;quot;alert(&#039;Hello World!&#039;)&amp;quot; to the web browser.&lt;br /&gt;
 //&lt;br /&gt;
 sendMessageF(string msg, list replacements)&lt;br /&gt;
 {&lt;br /&gt;
     integer numrepl = llGetListLength(replacements);&lt;br /&gt;
     integer i;&lt;br /&gt;
     for (i = 0; i &amp;amp;lt; numrepl; ++i) {&lt;br /&gt;
         msg = str_replace(msg, &amp;quot;{@&amp;quot; + (string)i + &amp;quot;}&amp;quot;, llList2String(replacements, i));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;sendMessage(msg);&amp;lt;/span&amp;gt;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E8E8FF&amp;quot;&amp;gt;// Chat logger demo: writes a new &amp;amp;lt;tr&amp;gt; table row to the web page&lt;br /&gt;
 // for every line of open chat it hears.&lt;br /&gt;
 //&lt;br /&gt;
 webAppInit()&lt;br /&gt;
 {&lt;br /&gt;
     string msg;&lt;br /&gt;
     string m0;&lt;br /&gt;
 &lt;br /&gt;
     // First, send over a few handy function definitions:&lt;br /&gt;
 &lt;br /&gt;
     msg = &amp;quot;function $$(t) { return document.getElementsByTagName(t)[0]; };&amp;quot;;&lt;br /&gt;
     msg += &amp;quot;function h() { return $$(&#039;head&#039;); };&amp;quot;;&lt;br /&gt;
     msg += &amp;quot;function b() { return $$(&#039;body&#039;); };&amp;quot;;&lt;br /&gt;
     msg += &amp;quot;function e(id) { return document.getElementById(id); };&amp;quot;;&lt;br /&gt;
     sendMessage(msg);&lt;br /&gt;
 &lt;br /&gt;
     // Send some CSS. WebKit is sensitive about appending &amp;amp;lt;style&amp;gt; elements&lt;br /&gt;
     // to &amp;amp;lt;head&amp;gt;, so we&#039;ll append it to an existing &amp;amp;lt;div&amp;gt; tag in &amp;amp;lt;body&amp;gt; instead.&lt;br /&gt;
 &lt;br /&gt;
     msg = &amp;quot;e(&#039;dv&#039;).innerHTML += \&amp;quot;{@0}\&amp;quot;;&amp;quot;;&lt;br /&gt;
     m0 = &amp;quot;&amp;amp;lt;style&amp;gt;td:nth-child(2) { text-align:right } tr:nth-child(odd) { background-color:#f8e8f8 }&amp;amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
     sendMessageF(msg, [m0]);&lt;br /&gt;
 &lt;br /&gt;
     // Write a &amp;amp;lt;table&amp;gt; element into element div#dv. The lines of chat will&lt;br /&gt;
     // become rows in this table appended to tbody#tbd&lt;br /&gt;
 &lt;br /&gt;
     msg = &amp;quot;e(&#039;dv&#039;).innerHTML += \&amp;quot;{@0}\&amp;quot;;&amp;quot;;&lt;br /&gt;
     m0 = &amp;quot;&amp;amp;lt;table&amp;gt;&amp;amp;lt;tbody id=&#039;tbd&#039;&amp;gt;&amp;amp;lt;/tbody&amp;gt;&amp;amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
     sendMessageF(msg, [m0]);&lt;br /&gt;
 &lt;br /&gt;
     llListen(0, &amp;quot;&amp;quot;, NULL_KEY, &amp;quot;&amp;quot;);&amp;lt;/span&amp;gt;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llClearPrimMedia(face);&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;        llRequestURL();&amp;lt;/span&amp;gt;&lt;br /&gt;
          &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E8E8FF&amp;quot;&amp;gt;        webAppInit();&amp;lt;/span&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;    http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             myURL = body;&lt;br /&gt;
             llOwnerSay(&amp;quot;myURL=&amp;quot; + myURL);&lt;br /&gt;
             setDataURI(myURL);&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             // Either send some queued messages now with llHTTPResponse(),&lt;br /&gt;
             // or if there&#039;s nothing to do now, save the GET id and&lt;br /&gt;
             // wait for somebody to call sendMessage().&amp;lt;/span&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;            if (llGetListLength(msgQueue) &amp;gt; 0) {&amp;lt;/span&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;                llHTTPResponse(id, 200, popQueuedMessages());&lt;br /&gt;
                 inId = NULL_KEY;&lt;br /&gt;
             } else {&lt;br /&gt;
                 inId = id;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E8E8FF&amp;quot;&amp;gt;    // When we hear chat from name, send a Javascript statement that&lt;br /&gt;
     // appends HTML to element #tbd. I.e., we&#039;ll make a string of HTML&lt;br /&gt;
     // formatted like this:&lt;br /&gt;
     //      &amp;amp;lt;tr style=&amp;quot;color:hsl(200,100%,30%)&amp;quot;&amp;gt;&lt;br /&gt;
     //         &amp;amp;lt;td&amp;gt;[01:23]&amp;amp;lt;/td&amp;gt;&lt;br /&gt;
     //         &amp;amp;lt;td&amp;gt;Avatar Name&amp;amp;lt;/td&amp;gt;&lt;br /&gt;
     //         &amp;amp;lt;td&amp;gt;the chat text&amp;amp;lt;/td&amp;gt;&lt;br /&gt;
     //      &amp;amp;lt;/tr&amp;gt;&lt;br /&gt;
     // and send it wrapped it in a Javascript statement like this:&lt;br /&gt;
     //      e(&#039;tbd&#039;).innerHTML += htmlstring;&lt;br /&gt;
     //&lt;br /&gt;
     listen(integer chan, string name, key id, string chat)&lt;br /&gt;
     {&lt;br /&gt;
         integer s = (integer)(&amp;quot;0x&amp;quot; + llGetSubString((string)id, 0, 6));&lt;br /&gt;
         string hue = (string)(s % 360);&lt;br /&gt;
         string color = &amp;quot;hsl(&amp;quot; + hue + &amp;quot;,100%, 30%)&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         string msg = &amp;quot;e(&#039;tbd&#039;).innerHTML += &#039;{@0}&#039;;&amp;quot;;&lt;br /&gt;
         string m0 = &amp;quot;&amp;amp;lt;tr style=\&amp;quot;color: {@4}\&amp;quot;&amp;gt;&amp;quot;;&lt;br /&gt;
         m0 +=   &amp;quot;&amp;amp;lt;td&amp;gt;{@1}&amp;amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
         m0 +=   &amp;quot;&amp;amp;lt;td&amp;gt;{@2}:&amp;amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
         m0 +=   &amp;quot;&amp;amp;lt;td&amp;gt;{@3}&amp;amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
         m0 += &amp;quot;&amp;amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         string t = llGetSubString(llGetTimestamp(), 11, 15);&lt;br /&gt;
         sendMessageF(msg, [m0, &amp;quot;[&amp;quot; + t + &amp;quot;]&amp;quot;, name, addSlashes(chat), color]);&lt;br /&gt;
     }&amp;lt;/span&amp;gt;&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
&amp;lt;ol start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;This &amp;quot;[http://en.wikipedia.org/wiki/Push_technology server-push]&amp;quot; technique using long-polling gives an LSL script the ability to modify the web page displayed on a prim. The Javascript side always keeps an HTTP GET open to the prim&#039;s HTTP-in URL, and the LSL side responds whenever it wants to with a response consisting of strings of Javascript to be executed by the web browser. For example, this LSL code causes an alert box to pop up on the web page:&lt;br /&gt;
: &amp;lt;code&amp;gt;sendMessage( &amp;quot;alert()&amp;quot; );&amp;lt;/code&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;In the LSL listing above, &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;the yellow highlighting&amp;lt;/span&amp;gt; shows the most important parts of the HTTP long-polling mechanism. The &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;green highlighting&amp;lt;/span&amp;gt; shows the message buffering on top of that. The &amp;lt;span style=&amp;quot;background-color:#E8E8FF&amp;quot;&amp;gt;blue highlighting&amp;lt;/span&amp;gt; is the application code that uses long-polling. To use long-polling for a different application, replace the blue parts. The rest is glue.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;To avoid some subtle WebKit problems when dynamically appending or replacing the first-level child nodes in &amp;amp;lt;head&amp;gt; or &amp;amp;lt;body&amp;gt;, we&#039;ve made two special &amp;amp;lt;div&amp;gt; elements on the bootstrap HTML page. The first surrounds the script#sc element that we replace for each GET. The second is at the end of &amp;amp;lt;body&amp;gt; as a convenient place for the application to insert new content that would otherwise go in &amp;amp;lt;body&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;To make sure that there is nearly always a valid GET request open, the Javascript side starts a new GET after receiving a response, or when the last GET times out.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;There are two timeouts hard-coded in the bootstrap data URI:&lt;br /&gt;
&amp;lt;ol style=&amp;quot;list-style-type:lower-roman&amp;quot;&amp;gt;&amp;lt;li&amp;gt;The &amp;quot;20000&amp;quot; is the timeout (20 seconds) for when an open GET expires, and should be set to something around the minimum of the WebKit outgoing GET request timeout, and the SL-LSL incoming GET timeout. This ensures that GETs are nearly continuous. [[LlHTTPResponse|This page]] says the timeout on the LSL side is 25 seconds.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;The &amp;quot;500&amp;quot; is a throttle that sets an upper limit to how fast the Javascript re-polls HTTP-in to prevent hammering the simulator. It&#039;s a half-second delay after receiving a GET response before starting a new GET.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; If there&#039;s a little gap between two GETs, or if two overlap a little, the sendMessage() message buffering will compensate by queuing up messages to be sent when the next GET arrives. If there are multiple messages in the queue when a GET arrives, the LSL side will concatenate as many messages as possible and send them together.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;On the Javascript side, the GET is not triggered until the &amp;lt;tt&amp;gt;&amp;amp;lt;script&amp;gt;&amp;lt;/tt&amp;gt; element is attached to its parent node with .appendChild() or .replaceChild().&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;The bootstrap Javascript above in setDataURI() had to be compressed somewhat to fit into the 1024-byte data URI limit. Here&#039;s an expanded listing for reference with more descriptive names:&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 &amp;amp;lt;!DOCTYPE HTML&amp;gt;&lt;br /&gt;
 &amp;amp;lt;html&amp;gt;&lt;br /&gt;
   &amp;amp;lt;body&amp;gt;&lt;br /&gt;
     &amp;amp;lt;div&amp;gt;&lt;br /&gt;
       &amp;amp;lt;script id=&#039;script&#039;&amp;gt; &amp;amp;lt;/script&amp;gt;&lt;br /&gt;
     &amp;amp;lt;/div&amp;gt;&lt;br /&gt;
     &amp;amp;lt;script&amp;gt;&lt;br /&gt;
       var poll=function(){&lt;br /&gt;
         var script=document.getElementById(&#039;script&#039;),&lt;br /&gt;
         timeoutId,&lt;br /&gt;
         seq=0,&lt;br /&gt;
         newScript;&lt;br /&gt;
         return {&lt;br /&gt;
           beg:function(){                                      // Initiate a long-poll GET&lt;br /&gt;
             newScript=document.createElement(&#039;script&#039;);        // The response will go here&lt;br /&gt;
             newScript.onload=poll.end;                         // Call poll.end() when we get a response&lt;br /&gt;
             timeoutId=setTimeout(&#039;poll.end()&#039;,20000);          // ... or if we time out&lt;br /&gt;
             newScript.src=&#039; HTTP-in URL goes here /?r=&#039;+(seq++);&lt;br /&gt;
             script.parentNode.replaceChild(newScript,script);  // this triggers the GET&lt;br /&gt;
             script=newScript;},&lt;br /&gt;
 &lt;br /&gt;
           end:function(){&lt;br /&gt;
             clearTimeout(timeoutId);&lt;br /&gt;
             timeoutId=null;&lt;br /&gt;
             script.onload=null;&lt;br /&gt;
             setTimeout(&#039;poll.beg()&#039;,500);                      // Wait a bit before re-polling&lt;br /&gt;
           },&lt;br /&gt;
         };&lt;br /&gt;
       }();&lt;br /&gt;
     &amp;amp;lt;/script&amp;gt;&lt;br /&gt;
     &amp;amp;lt;button id=&#039;btn&#039;onclick=poll.beg()&amp;gt;Start&amp;amp;lt;button&amp;gt;&lt;br /&gt;
     &amp;amp;lt;div id=&#039;dv&#039;&amp;gt; &amp;amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;amp;lt;/body&amp;gt;&lt;br /&gt;
 &amp;amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Make TinyURLs by script===&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 string myTinyURL;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             // Send our full URL to tinyurl.com for conversion&lt;br /&gt;
             // The answer will come back in http_response()&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPRequest(&amp;quot;http://tinyurl.com/api-create.php?url=&amp;quot; + body, [], &amp;quot;&amp;quot;);&amp;lt;/span&amp;gt;&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             llHTTPResponse(id, 200, &amp;quot;Hello Real World from the Virtual World&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_response(key req, integer stat, list met, string body)&lt;br /&gt;
     {&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;myTinyURL = body;&amp;lt;/span&amp;gt;&lt;br /&gt;
         llOwnerSay(&amp;quot;My HTTP-in TinyURL is: &amp;quot; + myTinyURL + &amp;quot; , Click Me!&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* Any URL or data URI can be mapped to a TinyURL. The example above reduces an assigned HTTP-in URL, which is a long one such as:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://sim4605.agni.lindenlab.com:12046/cap/2b9f06f7-431e-5b0f-9271-2d03bd15370b&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
into a TinyURL this size:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://tinyurl.com/y9etul3&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Development hints===&lt;br /&gt;
# You can develop your HTML and JavaScript outside of Second Life by putting your JavaScript incantations in data URIs that you feed to any web browser. For the most compatible browsers, use Safari or Google Chrome, or any other that uses the [[QtWebKit|WebKit]] HTML rendering engine. If you can&#039;t run Safari or Chrome, try the open source [http://code.google.com/p/arora/ Arora web browser] for simulating Media-on-a-Prim: it&#039;s based on straight-up WebKit and has the same handy inspector/debugger found in other WebKit browsers.&lt;br /&gt;
# If you don&#039;t have direct access to a web server while developing this stuff, then consider installing a local copy of [http://www.apache.org/ Apache web server] on your computer. You can restrict it to local access. Then you can refer to files on your own computer with URLs that start with &amp;lt;tt&amp;gt;&amp;lt;nowiki&amp;gt;http://localhost/&amp;lt;/nowiki&amp;gt;...&amp;lt;/tt&amp;gt;, and PHP scripts can simulate what the HTTP-in server would serve. That lets you, for example, edit &amp;lt;tt&amp;gt;myfile.js&amp;lt;/tt&amp;gt; on your own computer while working on a data URI that contains &amp;lt;tt&amp;gt;&amp;amp;lt;script src=&amp;quot;&amp;lt;nowiki&amp;gt;http://localhost/myfile.js&amp;lt;/nowiki&amp;gt;&amp;quot;&amp;gt;&amp;lt;/tt&amp;gt;. Then when &amp;lt;tt&amp;gt;myfile.js&amp;lt;/tt&amp;gt; is working, you can put the debugged code into an LSL script that will serve it through its HTTP-in port. That&#039;s easier than debugging all that in-world.&lt;br /&gt;
&lt;br /&gt;
===References and sources===&lt;br /&gt;
* [[User:Kelly_Linden/lsl_hacks|Kelly Linden&#039;s hacks]]&lt;br /&gt;
* [http://talirosca.wikidot.com/bootstrapping-html-on-a-prim Tali Rosca&#039;s techniques]&lt;br /&gt;
* [https://blogs.secondlife.com/message/115303 Notecard Text on a Prim - Demo]&lt;br /&gt;
* [[llSetPrimMediaParams]]()&lt;br /&gt;
* {{JIRA|SVC-3427}}&lt;br /&gt;
* [https://blogs.secondlife.com/message/124144 Ajax in Shared Media #1]&lt;br /&gt;
* [https://blogs.secondlife.com/thread/11698 Ajax in Shared Media #2]&lt;br /&gt;
* [https://blogs.secondlife.com/thread/12727 Ajax in Shared Media #3]&lt;br /&gt;
* [https://blogs.secondlife.com/thread/13455 SVG (Scalable Vector Graphics) in Shared Media]&lt;br /&gt;
* [[Shared_Media_and_data_URI|Shared_Media_and_data_URI]]&lt;br /&gt;
* [https://blogs.secondlife.com/community/community/tnt/blog/2010/03/17/viewer-2-tip-shared-media-add-custom-content-with-data-uri-no-webpage-upload-needed Shared Media: Add custom content with data: URI]&lt;br /&gt;
* [https://blogs.secondlife.com/message/119704 &amp;quot;Shared Media&amp;quot; - A few facts]&lt;br /&gt;
&lt;br /&gt;
[[Category:Shared Media]]&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Shared_Media_LSL_Recipes&amp;diff=875913</id>
		<title>User:Becky Pippen/Shared Media LSL Recipes</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Shared_Media_LSL_Recipes&amp;diff=875913"/>
		<updated>2010-04-23T06:09:55Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Added recipe for Reverse Ajax: Long-polling HTTP-in (chat logger example)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==How-to&#039;s with Media-on-a-Prim==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
  MediaWiki issue: If anyone knows how to highlight parts of code inside the &amp;lt;lsl&amp;gt;&lt;br /&gt;
  wrapper, let me know. I resorted to using a style attribute in a &amp;lt;span&amp;gt; element&lt;br /&gt;
  inside a &amp;lt;tt&amp;gt; element to yellow-highlight preformatted monospace text. It would&lt;br /&gt;
  be nicer if we could also get the LSL syntax highlighting, but if we have to choose&lt;br /&gt;
  one or the other, I think the yellow highlighting is more useful and instructive&lt;br /&gt;
  for this particular page than LSL syntax highlighting.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The examples here assume you&#039;re comfortable with LSL, and you just need a quick reference for the syntax of handling HTML and JavaScript in LSL for Shared Media scripting. All the examples on this page are ready to copy-n-paste and drop into a prim — no other setup needed. The key syntax in each example is highlighted. There&#039;s no error handling in these minimalist examples, so add plenty of your own.&lt;br /&gt;
&lt;br /&gt;
===Display plain text — XyText replacement===&lt;br /&gt;
[[Image:MoaP-example_1.png‎|thumb|Plain text]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;integer face = 4;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string message = &amp;quot;Hello World&amp;quot;;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llSetPrimMediaParams(face,&amp;lt;/span&amp;gt;&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;[PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message]);&amp;lt;/span&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* An avatar must press the Reload button on the browser to see this rendered in-world. A page will be auto-loaded if you include the parameter &amp;lt;tt&amp;gt;PRIM_MEDIA_AUTO_PLAY, TRUE&amp;lt;/tt&amp;gt;, and if the avatar has auto-load enabled in preferences.&lt;br /&gt;
* There&#039;s no need to add any %-hex-escaping or to use [[llEscapeURL]](); llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, url]) will automatically escape the string for you.&lt;br /&gt;
* This works by feeding a data URI to the on-prim browser. Think of it as serving the contents of the page in the URL itself. For more information, see [[Shared_Media_and_data_URI|here]] and [http://en.wikipedia.org/wiki/Data_URI_scheme here].&lt;br /&gt;
* The in-world browser limits the data URI (or any address) to 1024 bytes after %-hex-escaping and encoded in UTF-8. See below for tricks using external or self-served JavaScript or CSS to leverage the data URI.&lt;br /&gt;
* If the data URI contains just plain text, the prefix &amp;lt;tt&amp;gt;&amp;quot;data:text/html,&amp;quot;&amp;lt;/tt&amp;gt; can be abbreviated &amp;lt;tt&amp;gt;&amp;quot;data:,&amp;quot;&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Force a page reload===&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
[[Image:MoaP-example-refreshpage.gif|thumb|Auto-refresh]]&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string myURL;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;integer seq = 0; // sequence number for unique URLs&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             myURL = body;&lt;br /&gt;
             llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_AUTO_PLAY, TRUE,&lt;br /&gt;
                  PRIM_MEDIA_CURRENT_URL, myURL]);&lt;br /&gt;
             llSetTimerEvent(5.0);&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             llHTTPResponse(id, 200, &amp;quot;Sim FPS: &amp;quot; + (string)llGetRegionFPS());&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     timer()&lt;br /&gt;
     {&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_CURRENT_URL, myURL + &amp;quot;/?r=&amp;quot; + (string)(++seq)&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
Comments:&lt;br /&gt;
* The page will reload automatically if &amp;lt;tt&amp;gt;PRIM_MEDIA_AUTO_PLAY&amp;lt;/tt&amp;gt; is enabled and if the new &amp;lt;tt&amp;gt;PRIM_MEDIA_CURRENT_URL&amp;lt;/tt&amp;gt; is different. This technique appends a short dummy parameter (like &amp;lt;tt&amp;gt;&amp;quot;/?r=12&amp;quot;&amp;lt;/tt&amp;gt;) to the end of the URL to make it different each time, forcing a reload. The parameter is otherwise ignored.&lt;br /&gt;
&lt;br /&gt;
===Display text with HTML markup===&lt;br /&gt;
[[Image:MoaP-example-markup.png‎‎|thumb|HTML markup]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string message = &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;quot;&amp;amp;lt;i&amp;gt;Hello&amp;amp;lt;/i&amp;gt;&amp;amp;lt;h2&amp;gt;World!&amp;amp;lt;/h2&amp;gt;&amp;quot;&amp;lt;/span&amp;gt;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* The text of the data URI will be %-hex-escaped automatically for you, so there&#039;s no need to use [[llEscapeURL]](). All you need in the data URI is the prefix &amp;lt;tt&amp;gt;data:text/html,&amp;lt;/tt&amp;gt; plus your HTML.&lt;br /&gt;
&lt;br /&gt;
===Change window resolution===&lt;br /&gt;
[[Image:MoaP-example-size.png‎|thumb|Resized web window]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string message = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message,&lt;br /&gt;
                 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_WIDTH_PIXELS, 128&amp;lt;/span&amp;gt;,&lt;br /&gt;
                 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_HEIGHT_PIXELS, 32&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* The browser will add scroll bars if the rendered HTML doesn&#039;t fit within the specified PRIM_MEDIA_* size.&lt;br /&gt;
&lt;br /&gt;
===Display an image — works with animated GIFs too===&lt;br /&gt;
[[Image:MoaP-example-animgif.gif‎|thumb|Image on a prim]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL =&lt;br /&gt;
           &amp;quot;http://upload.wikimedia.org/wikipedia/commons/7/70/Rotating_earth_(small).gif&amp;quot;;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string dataURI = &amp;quot;data:text/html,&amp;amp;lt;object data=&#039;&amp;quot; + imageURL + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/object&amp;gt;&amp;quot;;&amp;lt;/span&amp;gt;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [PRIM_MEDIA_CURRENT_URL, dataURI,&lt;br /&gt;
              PRIM_MEDIA_WIDTH_PIXELS, 256,&lt;br /&gt;
              PRIM_MEDIA_HEIGHT_PIXELS, 256]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* If attempting to display YouTube video full-frame, see [https://blogs.secondlife.com/community/community/tnt/blog/2010/03/16/viewer-2-tip-shared-media-show-youtube-on-a-full-prims-face this discussion].&lt;br /&gt;
* For images, you can also use &amp;lt;tt&amp;gt;&amp;amp;lt;image src= &amp;gt;&amp;lt;/tt&amp;gt; like this:&lt;br /&gt;
 string dataURI=&amp;quot;data:text/html,&amp;lt;img src=&#039;&amp;quot; + imageURL + &amp;quot;&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
===Display a background image, tiled===&lt;br /&gt;
&lt;br /&gt;
====Method #1 — using CSS in a style element in &amp;lt;head&amp;gt;====&lt;br /&gt;
[[Image:MoaP-example-bgTiled.png‎|thumb|Tiled background image]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL =&lt;br /&gt;
             &amp;quot;http://www.google.com/intl/en_ALL/images/logo.gif&amp;quot;;&lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;amp;lt;head&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;style type=&#039;text/css&#039;&amp;gt;body{background-image:url(\&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;imageURL + &amp;quot;\&amp;quot;);}&amp;amp;lt;/style&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;amp;lt;body&amp;gt;Hello World&amp;amp;lt;/body&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Method #2  — using background attribute in &amp;amp;lt;body&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL = &amp;quot;http://www.google.com/intl/en_ALL/images/logo.gif&amp;quot;;&lt;br /&gt;
         string dataURI = &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;data:text/html,&amp;amp;lt;body background=&#039;&amp;quot; + imageURL + &amp;quot;&#039;&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;Hello World&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Method #3 — using style attribute in &amp;lt;body&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL = &amp;quot;http://www.google.com/intl/en_ALL/images/logo.gif&amp;quot;;&lt;br /&gt;
         string dataURI = &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;data:text/html,&amp;amp;lt;body style=&#039;background-image:url(\&amp;quot;&amp;quot; + imageURL + &amp;quot;\&amp;quot;)&#039;&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;Hello World&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Text colors — using &amp;amp;lt;font&amp;gt; element===&lt;br /&gt;
[[Image:MoaP-example-font-color.png‎|thumb|Font color]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string message = &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;font color=&#039;Red&#039;&amp;gt;Hello World&amp;amp;lt;/font&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 }  &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* You can also specify color and other CSS rules in a style attribute in another element. For example, you can replace message in the example above with:&lt;br /&gt;
&lt;br /&gt;
 string message = &amp;quot;&amp;amp;lt;h2 style=&#039;color:#FF0000&#039;&amp;gt;Hello World&amp;amp;lt;/h2&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
===Link to external CSS file (forms example)===&lt;br /&gt;
[[Image:MoaP-example-externalCSS.png‎|thumb|External CSS file]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string externalCSS =&amp;lt;/span&amp;gt;&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;quot;http://www.google.com/css/modules/buttons/g-button-chocobo.css&amp;quot;;&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;amp;lt;head&amp;gt;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;link href=&#039;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
                 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;externalCSS + &amp;quot;&#039; rel=&#039;stylesheet&#039; type=&#039;text/css&#039; /&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;quot; +&lt;br /&gt;
                 &amp;quot;&amp;amp;lt;form&amp;gt;&amp;amp;lt;input type=&#039;button&#039; value=&#039;Click Me&#039; class=&#039;g-button&#039; /&amp;gt;&amp;amp;lt;/form&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 }  &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* This is one way to work around the 1024-byte limitation of data URIs. The external CSS can be as large as the browser permits. Also see the examples below for putting JavaScript in an external file.&lt;br /&gt;
* The tag &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;lt;/tt&amp;gt; and the attribute &amp;lt;tt&amp;gt;type=&#039;text/css&#039;&amp;lt;/tt&amp;gt; may be omitted to make the data URI a few bytes shorter.&lt;br /&gt;
&lt;br /&gt;
===Calling external JavaScript functions (JQuery example)===&lt;br /&gt;
[[Image:MoaP-example-JQuery.gif‎|thumb|Calling external JQuery]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string externalJavascript =&lt;br /&gt;
     &amp;quot;http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js&amp;quot;;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;amp;lt;head&amp;gt;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;script src=&#039;&amp;quot; + externalJavascript + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;amp;lt;button&amp;gt;Toggle&amp;amp;lt;/button&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;amp;lt;p&amp;gt;Hello&amp;amp;lt;br /&amp;gt;World&amp;amp;lt;/p&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;script&amp;gt;$(&#039;button&#039;).click(function () &amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;{$(&#039;p&#039;).slideToggle(&#039;slow&#039;);});&amp;amp;lt;/script&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* This is a technique for working around the 1024-byte limit for data URIs. When you can&#039;t fit all the JavaScript you need into one data URI, just move some JavaScript functions into an external file and give the browser a data URI that references the external file with a URL. In this example above, the JavaScript for [http://jquery.com/ JQuery] lives in an external file on Google&#039;s servers. You just need enough bytes left over in the data URI to put a JavaScript function call with its parameters inside a &amp;lt;tt&amp;gt;&amp;amp;lt;script&amp;gt;&amp;lt;/tt&amp;gt; element.&lt;br /&gt;
* A data URI can be used as a springboard to generate arbitrary complex web pages by making use of these elements:&lt;br /&gt;
** a reference to external CSS with &amp;lt;tt&amp;gt;&amp;amp;lt;link href= &amp;lt;/tt&amp;gt;, &lt;br /&gt;
** a reference to external JavaScript with &amp;lt;tt&amp;gt;&amp;amp;lt;script src=&amp;lt;/tt&amp;gt;, and&lt;br /&gt;
** a function call with parameters to the external JavaScript using &amp;lt;tt&amp;gt;&amp;amp;lt;script&amp;gt;someFunction(args);&amp;amp;lt;/script&amp;gt;&amp;lt;/tt&amp;gt;. (In the JQuery example above, the name of the external function is &amp;lt;tt&amp;gt;&amp;quot;$&amp;quot;&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* See the next two examples for variations of this same technique.&lt;br /&gt;
&lt;br /&gt;
===Self-served HTML and JavaScript===&lt;br /&gt;
====Example #1 — using &amp;amp;lt;script src= and .innerHTML= ====&lt;br /&gt;
[[Image:MoaP-example-refresh-clock.gif‎|thumb|Self-served auto-refreshed pages]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string myURL;&lt;br /&gt;
 &lt;br /&gt;
 // This can be up to 2KBytes after %-hex-escaping:&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string servedPage =&amp;lt;/span&amp;gt; &amp;quot;&lt;br /&gt;
 function checklength(i){if (i&amp;lt;10) {i=&#039;0&#039;+i;} return i;}      &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;function clock()&amp;lt;/span&amp;gt;{ &lt;br /&gt;
  var now = new Date();  &lt;br /&gt;
  var hours = checklength(now.getHours());  &lt;br /&gt;
  var minutes = checklength(now.getMinutes());  &lt;br /&gt;
  var seconds = checklength(now.getSeconds());  &lt;br /&gt;
  var format = 1; //0=24 hour format, 1=12 hour format   &lt;br /&gt;
  var time;  &lt;br /&gt;
  if (format == 1) { &lt;br /&gt;
   if (hours &amp;gt;= 12) {  &lt;br /&gt;
    if (hours ==12 ) { hours = 12;&lt;br /&gt;
    } else { hours = hours-12; } &lt;br /&gt;
    time=hours+&#039;:&#039;+minutes+&#039;:&#039;+seconds+&#039; PM&#039;;   &lt;br /&gt;
   } else if(hours &amp;lt; 12) { &lt;br /&gt;
    if (hours ==0) {hours=12;}   &lt;br /&gt;
    time=hours+&#039;:&#039;+minutes+&#039;:&#039;+seconds+&#039; AM&#039;;   &lt;br /&gt;
   }   &lt;br /&gt;
  }  &lt;br /&gt;
  if (format == 0) {time= hours+&#039;:&#039;+minutes+&#039;:&#039;+seconds;}   &lt;br /&gt;
  &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;document.getElementById(&#039;clock&#039;).innerHTML=time;&amp;lt;/span&amp;gt;&lt;br /&gt;
  setTimeout(&#039;clock();&#039;, 500); &lt;br /&gt;
 } &amp;quot;; // yes, that&#039;s one long string.&lt;br /&gt;
 &lt;br /&gt;
 displayPage()&lt;br /&gt;
 {&lt;br /&gt;
     string dataURI = &amp;quot;data:text/html,&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;script src=&#039;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;myURL + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;div id=&#039;clock&#039;&amp;gt;&amp;amp;lt;script&amp;gt;clock();&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/div&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
     llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;myURL = body;&amp;lt;/span&amp;gt;&lt;br /&gt;
             displayPage();&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPResponse(id, 200, servedPage);&amp;lt;/span&amp;gt;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* This is a technique for trading the 1024-byte limit of data URIs for the 2048-byte-per-page limit that the script can serve itself. As in the previous example, when you can&#039;t fit all the JavaScript into one data URI, just move the JavaScript into an external file and reference the file with &amp;lt;tt&amp;gt;&amp;amp;lt;script src=URL&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;lt;/tt&amp;gt;. When the URL refers to the script&#039;s own HTTP-in URL, then the page served can contain up to 2K bytes, so you get at least that many bytes plus whatever logic you can fit into the rest of the data URI. If you move that JavaScript to an external web server, the size of the external JavaScript file can be as large as the client web browser can render.&lt;br /&gt;
* Your script can serve an arbitrary number of different pages through one HTTP-in URL by appending a path or parameter to the URL.&lt;br /&gt;
* This works by assigning a string containing HTML to the &amp;lt;tt&amp;gt;.innerHTML&amp;lt;/tt&amp;gt; contents of the &amp;lt;tt&amp;gt;&amp;amp;lt;div&amp;gt;&amp;lt;/tt&amp;gt; element with id &amp;quot;clock&amp;quot;. This achieves something similar to the Ajax-like [http://talirosca.wikidot.com/bootstrapping-html-on-a-prim technique published by Tali Rosca] and mentioned [http://wiki.secondlife.com/wiki/User:Kelly_Linden/lsl_hacks#DHTML_.2F_Javascript_.28Tali_Rosca.29 here]. In those techniques, a string is constructed containing an &amp;lt;tt&amp;gt;&amp;amp;lt;a href=&amp;lt;/tt&amp;gt; tag and an &amp;lt;tt&amp;gt;href=&amp;lt;/tt&amp;gt; that points to the script&#039;s HTTP-in URL. That string then replaces the &amp;lt;tt&amp;gt;&amp;amp;lt;body&amp;gt;&amp;lt;/tt&amp;gt; element using:&lt;br /&gt;
 &amp;lt;tt&amp;gt;document.getElementsByTagName(&#039;body&#039;)[0].innerHTML = &amp;lt;i&amp;gt;new-content&amp;lt;/i&amp;gt;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Example #2 — using &amp;lt;body onload= (lag graph example)====&lt;br /&gt;
[[Image:MoaP-example-laggraph.gif‎|thumb|Refreshed pages]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string jsURL; // where to fetch external JavaScript&lt;br /&gt;
 list numbers;&lt;br /&gt;
 integer numSamples = 50;&lt;br /&gt;
 &lt;br /&gt;
 // This is self-served in this example, but can be&lt;br /&gt;
 // moved to an external server:&lt;br /&gt;
 //&lt;br /&gt;
 string externalJavascript()&lt;br /&gt;
 {&lt;br /&gt;
     return&lt;br /&gt;
         &amp;quot;function bar(widthPct,heightPix) {&amp;quot; +&lt;br /&gt;
         &amp;quot; document.writeln(\&amp;quot;&amp;amp;lt;hr style=&#039;padding:0;margin:0;&amp;quot; +&lt;br /&gt;
         &amp;quot;  margin-top:-1px;text-align:left;align:left;border=0;&amp;quot; +&lt;br /&gt;
         &amp;quot;  width:\&amp;quot;+widthPct+\&amp;quot;%;height:\&amp;quot;+heightPix+\&amp;quot;px;&amp;quot; +&lt;br /&gt;
         &amp;quot;  background-color:#c22;color:#c22;&#039;&amp;gt;\&amp;quot;);}&amp;quot; +&lt;br /&gt;
         &amp;quot; function graphBars(arr){for(var i=0;i&amp;amp;lt;arr.length;++i)&amp;quot; +&lt;br /&gt;
         &amp;quot;  {bar(arr[i],18);}}&amp;quot;;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llClearPrimMedia(face);&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
         integer i = numSamples;&lt;br /&gt;
         while (--i &amp;gt;= 0) {&lt;br /&gt;
             numbers += 0;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     timer()&lt;br /&gt;
     {&lt;br /&gt;
         numbers = llList2List(numbers, 1, -1) + [(integer)(6.0 * (45.0 - llGetRegionFPS()))];&lt;br /&gt;
 &lt;br /&gt;
         // The dataURI loads external JavaScript functions and calls one with parameters:&lt;br /&gt;
 &lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;head&amp;gt;&amp;amp;lt;script src=&#039;&amp;quot; + jsURL + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;body onload=\&amp;quot;graphBars([&amp;quot; + llList2CSV(numbers) + &amp;quot;]);\&amp;quot;&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, dataURI, PRIM_MEDIA_AUTO_PLAY, TRUE]);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             jsURL = body; // self-serve the JavaScript&lt;br /&gt;
             llSetTimerEvent(1.0);&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             llHTTPResponse(id, 200, externalJavascript());&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* Like the previous example, this is another variation of how to structure a data URI to serve as a springboard for arbitrarily large pages. In this example, the data URI first uses a &amp;lt;tt&amp;gt;&amp;amp;lt;script src=...&amp;gt;&amp;lt;/tt&amp;gt; tag to read a file of external JavaScript functions in the &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;lt;/tt&amp;gt; element, and then the &amp;lt;tt&amp;gt;onload=&amp;lt;/tt&amp;gt; attribute in &amp;lt;tt&amp;gt;&amp;amp;lt;body&amp;gt;&amp;lt;/tt&amp;gt; triggers a call to one of the functions which executes a loop creating a bunch of &amp;lt;tt&amp;gt;&amp;amp;lt;hr&amp;gt;&amp;lt;/tt&amp;gt; elements that form the bar chart. The size of HTML generated by the JavaScript can be as large as the browser permits.&lt;br /&gt;
* In this example, &amp;lt;tt&amp;gt;jsURL&amp;lt;/tt&amp;gt; is set to the script&#039;s own HTTP-in URL so that the example can be self-contained, but you can point &amp;lt;tt&amp;gt;jsURL&amp;lt;/tt&amp;gt; to an external URL as well. If served by an external server, the JavaScript read in &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;lt;/tt&amp;gt; can be as large as the browser permits.&lt;br /&gt;
* &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;lt;/tt&amp;gt; can be omitted in the data URI surrounding the &amp;lt;tt&amp;gt;&amp;amp;lt;script src=&amp;lt;/tt&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
===Reverse Ajax — Long-polling the HTTP-in server (chat logger example)===&lt;br /&gt;
[[Image:MoaP-example-Long-polling-httpin.gif|thumb|300px|Long-polling HTTP-in]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 // Reverse Ajax: Long-polling HTTP-in.&lt;br /&gt;
 // Becky Pippen, 2010, contributed to the public domain.&lt;br /&gt;
 // http://wiki.secondlife.com/wiki/User:Becky_Pippen/MoaP_Snippets&lt;br /&gt;
 &lt;br /&gt;
 integer face = 4;          // Prim face for Shared Media&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string  myURL;             // HTTP-in URL&amp;lt;/span&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;key inId = NULL_KEY;       // GET request id&amp;lt;/span&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;list msgQueue = [];        // strings of Javascript&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;// url is our own HTTP-in url.&lt;br /&gt;
 // This sets up a bootloader web page like this:&lt;br /&gt;
 //      &amp;amp;lt;html&amp;gt;&amp;amp;lt;body&amp;gt;&lt;br /&gt;
 //         &amp;amp;lt;div&amp;gt;&amp;amp;lt;script id=&#039;sc&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/div&amp;gt;&lt;br /&gt;
 //         &amp;amp;lt;script&amp;gt; callbacks and poll.beg() defined here &amp;amp;lt;/script&amp;gt;&lt;br /&gt;
 //         &amp;amp;lt;button onclick=poll.beg()&amp;gt;Start&amp;amp;lt;/button&amp;gt;&lt;br /&gt;
 //         &amp;amp;lt;div id=&#039;dv&#039;&amp;gt;&amp;amp;lt;/div&amp;gt;&lt;br /&gt;
 //      &amp;amp;lt;/body&amp;gt;&amp;amp;lt;/html&amp;gt;&lt;br /&gt;
 // When the button is pressed, the JS code sets src= on script#sc&lt;br /&gt;
 // and reattaches the script element to the parent &amp;amp;lt;div&amp;gt; element which&lt;br /&gt;
 // initiates a GET to the prim&#039;s HTTP-in port&lt;br /&gt;
 //&lt;br /&gt;
 setDataURI(string url)&lt;br /&gt;
 {&lt;br /&gt;
     string dataURI = &amp;quot;data:text/html,&lt;br /&gt;
 &amp;amp;lt;!DOCTYPE HTML&amp;gt;&amp;amp;lt;html&amp;gt;&amp;amp;lt;body&amp;gt;&amp;amp;lt;div&amp;gt;&amp;amp;lt;script id=&#039;sc&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/div&amp;gt;&amp;amp;lt;script&amp;gt;&lt;br /&gt;
 var poll=function(){var sc=document.getElementById(&#039;sc&#039;),t2,seq=0,s0;return{&lt;br /&gt;
 beg:function(){s0=document.createElement(&#039;script&#039;);s0.onload=poll.end;t2=setTimeout(&#039;poll.end()&#039;,20000);&lt;br /&gt;
 s0.src=&#039;&amp;quot; + url + &amp;quot;/?r=&#039;+(seq++);sc.parentNode.replaceChild(s0,sc);sc=s0;},&lt;br /&gt;
 end:function(){clearTimeout(t2);t2=null;sc.onload=null;setTimeout(&#039;poll.beg()&#039;,500);},};}();&amp;amp;lt;/script&amp;gt;&lt;br /&gt;
 &amp;amp;lt;button id=&#039;btn&#039;onclick=poll.beg()&amp;gt;Start&amp;amp;lt;/button&amp;gt;&amp;amp;lt;div id=&#039;dv&#039;&amp;gt;&amp;amp;lt;/div&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;amp;lt;/html&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
     llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, dataURI]);&amp;lt;/span&amp;gt;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;// Returns zero or more queued messages. Assumes no single message is&lt;br /&gt;
 // longer than MAX_SIZE_CHARS (will hang if there is)&lt;br /&gt;
 //&lt;br /&gt;
 string popQueuedMessages()&lt;br /&gt;
 {&lt;br /&gt;
     string  totalMsg = &amp;quot;&amp;quot;;&lt;br /&gt;
     integer totalMsgSize = 0;&lt;br /&gt;
     string  nextMsg = &amp;quot;&amp;quot;;&lt;br /&gt;
     integer nextMsgSize = 0;&lt;br /&gt;
 &lt;br /&gt;
     // HTTP response bodies are limited to 2048 bytes after encoding&lt;br /&gt;
     // in UTF-8. LSL string sizes are measured in characters, which,&lt;br /&gt;
     // in UTF-8, use one byte (for ASCII chars), two bytes (most Latin-1),&lt;br /&gt;
     // or three bytes (a few international characters). So, unless&lt;br /&gt;
     // you re-write this section so that it measures UTF-8 size, keep&lt;br /&gt;
     // MAX_SIZE_CHARS small enough so the text will fit in a response body.&lt;br /&gt;
     //&lt;br /&gt;
     integer MAX_SIZE_CHARS = 1000; // Max HTTP body size &lt;br /&gt;
  &lt;br /&gt;
     integer numMessagesQueued = llGetListLength(msgQueue);&lt;br /&gt;
     integer count = 0;&lt;br /&gt;
     while (count &amp;amp;lt; numMessagesQueued) {&lt;br /&gt;
         nextMsg = llList2String(msgQueue, count);&lt;br /&gt;
         nextMsgSize = llStringLength(nextMsg);&lt;br /&gt;
 &lt;br /&gt;
         if (totalMsgSize + nextMsgSize &amp;amp;lt; MAX_SIZE_CHARS) {&lt;br /&gt;
             totalMsg += nextMsg;&lt;br /&gt;
             totalMsgSize += nextMsgSize;&lt;br /&gt;
             ++count;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     // Delete the messages from the queue that we&#039;re going to send:&lt;br /&gt;
     if (count &amp;gt; 0) {&lt;br /&gt;
         msgQueue = llDeleteSubList(msgQueue, 0, count - 1);&amp;lt;/span&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return totalMsg;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;// Called when there are previous messages still queued, or if there&lt;br /&gt;
 // is no GET request currently open to respond to.&amp;lt;/span&amp;gt;&lt;br /&gt;
 //&lt;br /&gt;
 pushMessageToSend(string msg)&lt;br /&gt;
 {&lt;br /&gt;
     &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;msgQueue = msgQueue + [msg]; // last element is the last one stacked&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
     // See if we can send some messages now:&lt;br /&gt;
     &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;if (inId != NULL_KEY) {&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPResponse(inId, 200, popQueuedMessages());&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;inId = NULL_KEY;&amp;lt;/span&amp;gt;&lt;br /&gt;
     } // else wait for the next incoming GET request&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Replaces all occurrences of &#039;from&#039; with &#039;to&#039; in &#039;src&#039;&lt;br /&gt;
 // From http://snipplr.com/view/13279/lslstrreplace/&lt;br /&gt;
 //&lt;br /&gt;
 string str_replace(string subject, string search, string replace)&lt;br /&gt;
 {&lt;br /&gt;
     return llDumpList2String(llParseStringKeepNulls(subject, [search], []), replace);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Optionally filter out characters in text that would mess up the&lt;br /&gt;
 // web page display. This demo just escapes &#039; and &amp;quot; and adds a space after &#039;&amp;lt;&#039;.&lt;br /&gt;
 //&lt;br /&gt;
 string addSlashes(string s)&lt;br /&gt;
 {&lt;br /&gt;
     return str_replace(str_replace(str_replace(s, &amp;quot;&amp;amp;lt;&amp;quot;, &amp;quot;&amp;amp;lt; &amp;quot;), &amp;quot;\&amp;quot;&amp;quot;, &amp;quot;\\\&amp;quot;&amp;quot;), &amp;quot;&#039;&amp;quot;, &amp;quot;\\\&#039;&amp;quot;);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // This is the main interface for LSL to control the Shared Media web page.&lt;br /&gt;
 // The messages we send consist of Javascript function statements that the&lt;br /&gt;
 // browser will evaluate and execute in the context of the web page.&lt;br /&gt;
 // See sendMessageF() for a similar function with macro replacement.&lt;br /&gt;
 //&lt;br /&gt;
 sendMessage(string msg)&lt;br /&gt;
 {&lt;br /&gt;
     // Test for the easy case: if there are no other messages waiting&lt;br /&gt;
     // in the queue, and if there is an open GET connection, then just&lt;br /&gt;
     // respond immediately:&lt;br /&gt;
  &lt;br /&gt;
     &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;if (llGetListLength(msgQueue) == 0&amp;lt;/span&amp;gt; &amp;amp;&amp;amp; &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;inId != NULL_KEY) {&amp;lt;/span&amp;gt;&lt;br /&gt;
         // Nothing in the queue and an open GET, so respond immediately:&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPResponse(inId, 200, msg);&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;inId = NULL_KEY;&amp;lt;/span&amp;gt;&lt;br /&gt;
     } &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;else {&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;pushMessageToSend(msg);&amp;lt;/span&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Same as sendMessage() but with macro replacements. For each nth string&lt;br /&gt;
 // element in replacements, replace all occurrences of {@n}. For example,&lt;br /&gt;
 //     sendMessageF( &amp;quot;alert(&#039;{@0} {@1}!&#039;)&amp;quot;, [&amp;quot;Hello&amp;quot;, &amp;quot;World&amp;quot;] );&lt;br /&gt;
 // will send &amp;quot;alert(&#039;Hello World!&#039;)&amp;quot; to the web browser.&lt;br /&gt;
 //&lt;br /&gt;
 sendMessageF(string msg, list replacements)&lt;br /&gt;
 {&lt;br /&gt;
     integer numrepl = llGetListLength(replacements);&lt;br /&gt;
     integer i;&lt;br /&gt;
     for (i = 0; i &amp;amp;lt; numrepl; ++i) {&lt;br /&gt;
         msg = str_replace(msg, &amp;quot;{@&amp;quot; + (string)i + &amp;quot;}&amp;quot;, llList2String(replacements, i));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;sendMessage(msg);&amp;lt;/span&amp;gt;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E8E8FF&amp;quot;&amp;gt;// Chat logger demo: writes a new &amp;amp;lt;tr&amp;gt; table row to the web page&lt;br /&gt;
 // for every line of open chat it hears.&lt;br /&gt;
 //&lt;br /&gt;
 webAppInit()&lt;br /&gt;
 {&lt;br /&gt;
     string msg;&lt;br /&gt;
     string m0;&lt;br /&gt;
 &lt;br /&gt;
     // First, send over a few handy function definitions:&lt;br /&gt;
 &lt;br /&gt;
     msg = &amp;quot;function $$(t) { return document.getElementsByTagName(t)[0]; };&amp;quot;;&lt;br /&gt;
     msg += &amp;quot;function h() { return $$(&#039;head&#039;); };&amp;quot;;&lt;br /&gt;
     msg += &amp;quot;function b() { return $$(&#039;body&#039;); };&amp;quot;;&lt;br /&gt;
     msg += &amp;quot;function e(id) { return document.getElementById(id); };&amp;quot;;&lt;br /&gt;
     sendMessage(msg);&lt;br /&gt;
 &lt;br /&gt;
     // Send some CSS. WebKit is sensitive about appending &amp;amp;lt;style&amp;gt; elements&lt;br /&gt;
     // to &amp;amp;lt;head&amp;gt;, so we&#039;ll append it to an existing &amp;amp;lt;div&amp;gt; tag in &amp;amp;lt;body&amp;gt; instead.&lt;br /&gt;
 &lt;br /&gt;
     msg = &amp;quot;e(&#039;dv&#039;).innerHTML += \&amp;quot;{@0}\&amp;quot;;&amp;quot;;&lt;br /&gt;
     m0 = &amp;quot;&amp;amp;lt;style&amp;gt;td:nth-child(2) { text-align:right } tr:nth-child(odd) { background-color:#f8e8f8 }&amp;amp;lt;/style&amp;gt;&amp;quot;;&lt;br /&gt;
     sendMessageF(msg, [m0]);&lt;br /&gt;
 &lt;br /&gt;
     // Write a &amp;amp;lt;table&amp;gt; element into element div#dv. The lines of chat will&lt;br /&gt;
     // become rows in this table appended to tbody#tbd&lt;br /&gt;
 &lt;br /&gt;
     msg = &amp;quot;e(&#039;dv&#039;).innerHTML += \&amp;quot;{@0}\&amp;quot;;&amp;quot;;&lt;br /&gt;
     m0 = &amp;quot;&amp;amp;lt;table&amp;gt;&amp;amp;lt;tbody id=&#039;tbd&#039;&amp;gt;&amp;amp;lt;/tbody&amp;gt;&amp;amp;lt;/table&amp;gt;&amp;quot;;&lt;br /&gt;
     sendMessageF(msg, [m0]);&lt;br /&gt;
 &lt;br /&gt;
     llListen(0, &amp;quot;&amp;quot;, NULL_KEY, &amp;quot;&amp;quot;);&amp;lt;/span&amp;gt;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llClearPrimMedia(face);&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;        llRequestURL();&amp;lt;/span&amp;gt;&lt;br /&gt;
          &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E8E8FF&amp;quot;&amp;gt;        webAppInit();&amp;lt;/span&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;    http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             myURL = body;&lt;br /&gt;
             llOwnerSay(&amp;quot;myURL=&amp;quot; + myURL);&lt;br /&gt;
             setDataURI(myURL);&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             // Either send some queued messages now with llHTTPResponse(),&lt;br /&gt;
             // or if there&#039;s nothing to do now, save the GET id and&lt;br /&gt;
             // wait for somebody to call sendMessage().&amp;lt;/span&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;            if (llGetListLength(msgQueue) &amp;gt; 0) {&amp;lt;/span&amp;gt;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;                llHTTPResponse(id, 200, popQueuedMessages());&lt;br /&gt;
                 inId = NULL_KEY;&lt;br /&gt;
             } else {&lt;br /&gt;
                 inId = id;&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
     }&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#E8E8FF&amp;quot;&amp;gt;    // When we hear chat from name, send a Javascript statement that&lt;br /&gt;
     // appends HTML to element #tbd. I.e., we&#039;ll make a string of HTML&lt;br /&gt;
     // formatted like this:&lt;br /&gt;
     //      &amp;amp;lt;tr style=&amp;quot;color:hsl(200,100%,30%)&amp;quot;&amp;gt;&lt;br /&gt;
     //         &amp;amp;lt;td&amp;gt;[01:23]&amp;amp;lt;/td&amp;gt;&lt;br /&gt;
     //         &amp;amp;lt;td&amp;gt;Avatar Name&amp;amp;lt;/td&amp;gt;&lt;br /&gt;
     //         &amp;amp;lt;td&amp;gt;the chat text&amp;amp;lt;/td&amp;gt;&lt;br /&gt;
     //      &amp;amp;lt;/tr&amp;gt;&lt;br /&gt;
     // and send it wrapped it in a Javascript statement like this:&lt;br /&gt;
     //      e(&#039;tbd&#039;).innerHTML += htmlstring;&lt;br /&gt;
     //&lt;br /&gt;
     listen(integer chan, string name, key id, string chat)&lt;br /&gt;
     {&lt;br /&gt;
         integer s = (integer)(&amp;quot;0x&amp;quot; + llGetSubString((string)id, 0, 6));&lt;br /&gt;
         string hue = (string)(s % 360);&lt;br /&gt;
         string color = &amp;quot;hsl(&amp;quot; + hue + &amp;quot;,100%, 30%)&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         string msg = &amp;quot;e(&#039;tbd&#039;).innerHTML += &#039;{@0}&#039;;&amp;quot;;&lt;br /&gt;
         string m0 = &amp;quot;&amp;amp;lt;tr style=\&amp;quot;color: {@4}\&amp;quot;&amp;gt;&amp;quot;;&lt;br /&gt;
         m0 +=   &amp;quot;&amp;amp;lt;td&amp;gt;{@1}&amp;amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
         m0 +=   &amp;quot;&amp;amp;lt;td&amp;gt;{@2}:&amp;amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
         m0 +=   &amp;quot;&amp;amp;lt;td&amp;gt;{@3}&amp;amp;lt;/td&amp;gt;&amp;quot;;&lt;br /&gt;
         m0 += &amp;quot;&amp;amp;lt;/tr&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         string t = llGetSubString(llGetTimestamp(), 11, 15);&lt;br /&gt;
         sendMessageF(msg, [m0, &amp;quot;[&amp;quot; + t + &amp;quot;]&amp;quot;, name, addSlashes(chat), color]);&lt;br /&gt;
     }&amp;lt;/span&amp;gt;&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
&amp;lt;ol start=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;This &amp;quot;[http://en.wikipedia.org/wiki/Push_technology server-push]&amp;quot; technique using long-polling gives an LSL script the ability to modify the web page displayed on a prim. The Javascript side always keeps an HTTP GET open to the prim&#039;s HTTP-in URL, and the LSL side responds whenever it wants to with a response consisting of strings of Javascript to be executed by the web browser. For example, this LSL code causes an alert box to pop up on the web page:&lt;br /&gt;
: &amp;lt;code&amp;gt;sendMessage( &amp;quot;alert()&amp;quot; );&amp;lt;/code&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;In the LSL listing above, &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;the yellow highlighting&amp;lt;/span&amp;gt; shows the most important parts of the HTTP long-polling mechanism. The &amp;lt;span style=&amp;quot;background-color:#E0FDE0&amp;quot;&amp;gt;green highlighting&amp;lt;/span&amp;gt; shows the message buffering on top of that. The &amp;lt;span style=&amp;quot;background-color:#E8E8FF&amp;quot;&amp;gt;blue highlighting&amp;lt;/span&amp;gt; is the application code that uses long-polling. To use long-polling for a different application, replace the blue parts. The rest is glue.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;To avoid some subtle WebKit problems when dynamically appending or replacing the first-level child nodes in &amp;amp;lt;head&amp;gt; or &amp;amp;lt;body&amp;gt;, we&#039;ve made two special &amp;amp;lt;div&amp;gt; elements on the bootstrap HTML page. The first surrounds the script#sc element that we replace for each GET. The second is at the end of &amp;amp;lt;body&amp;gt; as a convenient place for the application to insert new content that would otherwise go in &amp;amp;lt;body&amp;gt;.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;To make sure that there is nearly always a valid GET request open, the Javascript side starts a new GET after receiving a response, or when the last GET times out.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;There are two timeouts hard-coded in the bootstrap data URI:&lt;br /&gt;
&amp;lt;ol style=&amp;quot;list-style-type:lower-roman&amp;quot;&amp;gt;&amp;lt;li&amp;gt;The &amp;quot;20000&amp;quot; is the timeout (20 seconds) for when an open GET expires, and should be set to something around the minimum of the WebKit outgoing GET request timeout, and the SL-LSL incoming GET timeout. This ensures that GETs are nearly continuous. [[LlHTTPResponse|This page]] says the timeout on the LSL side is 25 seconds.&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;The &amp;quot;500&amp;quot; is a throttle that sets an upper limit to how fast the Javascript re-polls HTTP-in to prevent hammering the simulator. It&#039;s a half-second delay after receiving a GET response before starting a new GET.&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt; If there&#039;s a little gap between two GETs, or if two overlap a little, the sendMessage() message buffering will compensate by queuing up messages to be sent when the next GET arrives. If there are multiple messages in the queue when a GET arrives, the LSL side will concatenate as many messages as possible and send them together.&lt;br /&gt;
&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;On the Javascript side, the GET is not triggered until the &amp;lt;tt&amp;gt;&amp;amp;lt;script&amp;gt;&amp;lt;/tt&amp;gt; element is attached to its parent node with .appendChild() or .replaceChild().&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;The bootstrap Javascript above in setDataURI() had to be compressed somewhat to fit into the 1024-byte data URI limit. Here&#039;s an expanded listing for reference with more descriptive names:&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 &amp;amp;lt;!DOCTYPE HTML&amp;gt;&lt;br /&gt;
 &amp;amp;lt;html&amp;gt;&lt;br /&gt;
   &amp;amp;lt;body&amp;gt;&lt;br /&gt;
     &amp;amp;lt;div&amp;gt;&lt;br /&gt;
       &amp;amp;lt;script id=&#039;script&#039;&amp;gt; &amp;amp;lt;/script&amp;gt;&lt;br /&gt;
     &amp;amp;lt;/div&amp;gt;&lt;br /&gt;
     &amp;amp;lt;script&amp;gt;&lt;br /&gt;
       var poll=function(){&lt;br /&gt;
         var script=document.getElementById(&#039;script&#039;),&lt;br /&gt;
         timeoutId,&lt;br /&gt;
         seq=0,&lt;br /&gt;
         newScript;&lt;br /&gt;
         return {&lt;br /&gt;
           beg:function(){                                      // Initiate a long-poll GET&lt;br /&gt;
             newScript=document.createElement(&#039;script&#039;);        // The response will go here&lt;br /&gt;
             newScript.onload=poll.end;                         // Call poll.end() when we get a response&lt;br /&gt;
             timeoutId=setTimeout(&#039;poll.end()&#039;,20000);          // ... or if we time out&lt;br /&gt;
             newScript.src=&#039; HTTP-in URL goes here /?r=&#039;+(seq++);&lt;br /&gt;
             script.parentNode.replaceChild(newScript,script);  // this triggers the GET&lt;br /&gt;
             script=newScript;},&lt;br /&gt;
 &lt;br /&gt;
           end:function(){&lt;br /&gt;
             clearTimeout(timeoutId);&lt;br /&gt;
             timeoutId=null;&lt;br /&gt;
             script.onload=null;&lt;br /&gt;
             setTimeout(&#039;poll.beg()&#039;,500);                      // Wait a bit before re-polling&lt;br /&gt;
           },&lt;br /&gt;
         };&lt;br /&gt;
       }();&lt;br /&gt;
     &amp;amp;lt;/script&amp;gt;&lt;br /&gt;
     &amp;amp;lt;button id=&#039;btn&#039;onclick=poll.beg()&amp;gt;Start&amp;amp;lt;button&amp;gt;&lt;br /&gt;
     &amp;amp;lt;div id=&#039;dv&#039;&amp;gt; &amp;amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;amp;lt;/body&amp;gt;&lt;br /&gt;
 &amp;amp;lt;/html&amp;gt;&lt;br /&gt;
&amp;lt;/tt&amp;gt;&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Make TinyURLs by script===&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 string myTinyURL;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             // Send our full URL to tinyurl.com for conversion&lt;br /&gt;
             // The answer will come back in http_response()&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPRequest(&amp;quot;http://tinyurl.com/api-create.php?url=&amp;quot; + body, [], &amp;quot;&amp;quot;);&amp;lt;/span&amp;gt;&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             llHTTPResponse(id, 200, &amp;quot;Hello Real World from the Virtual World&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_response(key req, integer stat, list met, string body)&lt;br /&gt;
     {&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;myTinyURL = body;&amp;lt;/span&amp;gt;&lt;br /&gt;
         llOwnerSay(&amp;quot;My HTTP-in TinyURL is: &amp;quot; + myTinyURL + &amp;quot; , Click Me!&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* Any URL or data URI can be mapped to a TinyURL. The example above reduces an assigned HTTP-in URL, which is a long one such as:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://sim4605.agni.lindenlab.com:12046/cap/2b9f06f7-431e-5b0f-9271-2d03bd15370b&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
into a TinyURL this size:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://tinyurl.com/y9etul3&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Development hints===&lt;br /&gt;
# You can develop your HTML and JavaScript outside of Second Life by putting your JavaScript incantations in data URIs that you feed to any web browser. For the most compatible browsers, use Safari or Google Chrome, or any other that uses the [[QtWebKit|WebKit]] HTML rendering engine. If you can&#039;t run Safari or Chrome, try the open source [http://code.google.com/p/arora/ Arora web browser] for simulating Media-on-a-Prim: it&#039;s based on straight-up WebKit and has the same handy inspector/debugger found in other WebKit browsers.&lt;br /&gt;
# If you don&#039;t have direct access to a web server while developing this stuff, then consider installing a local copy of [http://www.apache.org/ Apache web server] on your computer. You can restrict it to local access. Then you can refer to files on your own computer with URLs that start with &amp;lt;tt&amp;gt;&amp;lt;nowiki&amp;gt;http://localhost/&amp;lt;/nowiki&amp;gt;...&amp;lt;/tt&amp;gt;, and PHP scripts can simulate what the HTTP-in server would serve. That lets you, for example, edit &amp;lt;tt&amp;gt;myfile.js&amp;lt;/tt&amp;gt; on your own computer while working on a data URI that contains &amp;lt;tt&amp;gt;&amp;amp;lt;script src=&amp;quot;&amp;lt;nowiki&amp;gt;http://localhost/myfile.js&amp;lt;/nowiki&amp;gt;&amp;quot;&amp;gt;&amp;lt;/tt&amp;gt;. Then when &amp;lt;tt&amp;gt;myfile.js&amp;lt;/tt&amp;gt; is working, you can put the debugged code into an LSL script that will serve it through its HTTP-in port. That&#039;s easier than debugging all that in-world.&lt;br /&gt;
&lt;br /&gt;
===References and sources===&lt;br /&gt;
* [[User:Kelly_Linden/lsl_hacks|Kelly Linden&#039;s hacks]]&lt;br /&gt;
* [http://talirosca.wikidot.com/bootstrapping-html-on-a-prim Tali Rosca&#039;s techniques]&lt;br /&gt;
* [https://blogs.secondlife.com/message/115303 Notecard Text on a Prim - Demo]&lt;br /&gt;
* [[llSetPrimMediaParams]]()&lt;br /&gt;
* {{JIRA|SVC-3427}}&lt;br /&gt;
* [https://blogs.secondlife.com/message/124144 Ajax in Shared Media #1]&lt;br /&gt;
* [https://blogs.secondlife.com/thread/11698 Ajax in Shared Media #2]&lt;br /&gt;
* [https://blogs.secondlife.com/thread/12727 Ajax in Shared Media #3]&lt;br /&gt;
* [https://blogs.secondlife.com/thread/13455 SVG (Scalable Vector Graphics) in Shared Media]&lt;br /&gt;
* [[Shared_Media_and_data_URI|Shared_Media_and_data_URI]]&lt;br /&gt;
* [https://blogs.secondlife.com/community/community/tnt/blog/2010/03/17/viewer-2-tip-shared-media-add-custom-content-with-data-uri-no-webpage-upload-needed Shared Media: Add custom content with data: URI]&lt;br /&gt;
* [https://blogs.secondlife.com/message/119704 &amp;quot;Shared Media&amp;quot; - A few facts]&lt;br /&gt;
&lt;br /&gt;
[[Category:Shared Media]]&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-Long-polling-httpin.gif&amp;diff=875873</id>
		<title>File:MoaP-example-Long-polling-httpin.gif</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-Long-polling-httpin.gif&amp;diff=875873"/>
		<updated>2010-04-23T06:02:07Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Media-on-a-Prim example of Long-polling the prim&amp;#039;s own HTTP server&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Media-on-a-Prim example of Long-polling the prim&#039;s own HTTP server&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen&amp;diff=817412</id>
		<title>User:Becky Pippen</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen&amp;diff=817412"/>
		<updated>2010-03-22T23:01:58Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Add link to User:Becky_Pippen/Shared_Media_LSL_Recipes&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== About ==&lt;br /&gt;
I love helping new residents get addicted... er, I mean acclimated to Second Life. Here are some notes I&#039;ve picked up along the way that might be of help.&lt;br /&gt;
== SL Glossary ==&lt;br /&gt;
[http://www.inkwelle.com/glossary_nci.shtml Second Life&#039;s most complete glossary]&lt;br /&gt;
== What is Mono? ==&lt;br /&gt;
=== John and Mary ===&lt;br /&gt;
John and Mary are LSL scripters. John prefers the old-fashioned way of doing things, while Mary enjoys all the latest new technology, like Mono. They both write LSL scripts. John runs his scripts under the old way, and Mary runs hers using Mono.&lt;br /&gt;
&lt;br /&gt;
When John writes an LSL script and clicks the Save button, the client viewer compiles the script into proprietary bytecode and uploads it to the servers. The server runs the script by using a proprietary interpreter to interpret the bytecode. It runs slow.&lt;br /&gt;
&lt;br /&gt;
Mary writes the identical script -- no change in the LSL language syntax. When she presses &amp;quot;Save&amp;quot;, the client uploads her script text to the servers where it gets compiled into standardized CIL assembly language. (CIL is a bytecode that originally came from Microsoft&#039;s .NET technology.) Because the Linden servers are Linux machines, there&#039;s no .NET framework  available to run the CIL code. So the servers use the open-source Mono framework to execute the CIL. Mono includes an open-source JIT (&amp;quot;Just in Time&amp;quot;) compiler that converts the CIL bytecode into machine code as it&#039;s needed, and that compiled machine code is what makes the script execute faster than the old way.&lt;br /&gt;
=== How it all Flows ===&lt;br /&gt;
Old way:&lt;br /&gt;
 LSL  ---&amp;gt; LSL compiler ---&amp;gt; proprietary bytecode ---&amp;gt; LSL bytecode interpreter&lt;br /&gt;
&lt;br /&gt;
New way:&lt;br /&gt;
 LSL                        ---&amp;gt; LSL compiler   | &lt;br /&gt;
 C# (future)                ---&amp;gt; C# compiler    |  ---&amp;gt; CIL bytecode ---&amp;gt; Mono CIL execution engine&lt;br /&gt;
 Other languages (future)   ---&amp;gt; other compiler |&lt;br /&gt;
=== Do Scripts Running in Mono Get More Memory? ===&lt;br /&gt;
Yes, it&#039;s true that each Mono script gets 64K of memory to use. Non-Mono scripts have to fit inside 16K of memory. (That&#039;s compiled code plus stack plus heap.) Unfortunately, Mono-compiled code is a bit of a memory hog and can take up to 4 times as much memory as the old runtime environment. So, scripts using Mono end up with a little more memory to play with depending on the code-to-data ratio, but not four times as much.&lt;br /&gt;
=== Evolving Terminology ===&lt;br /&gt;
LSL - Used to refer to the whole system -- the language, the front-end compiler, and the back-end interpreter. Now it&#039;s confusing because it&#039;s used in one of two ways and its meaning depends on the context:  (1) LSL is the language, the source code, which has not changed with Mono. E.g., &amp;quot;Take this LSL script and compile it with Mono and it will run faster.&amp;quot; Or (2) LSL refers to the old compiler front-end and interpreter virtual machine back-end. E.g., &amp;quot;When I compile this script with LSL it runs very slowly.&amp;quot; Both meanings are used in the sentence, &amp;quot;This LSL script was compiled with LSL, but that LSL script was compiled with Mono.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The term LSO refers to the bytecode generated by the traditional LSL compiler and executed by the old LSL runtime, and is a term that can be used to distinguish the old LSL tool set from the Mono tool set.&lt;br /&gt;
=== Can We Use Other Languages? ===&lt;br /&gt;
Mono just runs CIL bytecode. It doesn&#039;t know or care what the original language was. At this time (mid-2008) only LSL source code can be compiled into CIL and run on Mono. But maybe someday we will be able to compile C# and possibly other languages into CIL which will run under Mono. A single object could contain scripts that originally were written in different languages. &lt;br /&gt;
&lt;br /&gt;
The LSL language -- the source syntax -- will probably be supported for a very long time. &lt;br /&gt;
&lt;br /&gt;
However, the servers will support both back-ends for a very long time -- the one that executes the old proprietary LSL bytecode, and the new Mono virtual machine that executes standard CIL. All the millions of existing LSL scripts compiled the old way will continue to run indefinitely. New LSL scripts may be compiled using the old LSL compiler or the new Mono compiler.&lt;br /&gt;
=== What To Expect After Mono Hits the Main Grid ===&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;Old LSL scripts already compiled:&#039;&#039;&#039;&#039;&#039; will continue to run indefinitely. You don&#039;t have to do a thing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;Any LSL script if you have the source code:&#039;&#039;&#039;&#039;&#039; can be recompiled under the old LSL compiler or the new Mono compiler, at your choice, for a long time to come.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;C# and other languages:&#039;&#039;&#039;&#039;&#039; probably added at some future time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== LSL Timings and Rates ==&lt;br /&gt;
Updated and [[User:Becky_Pippen/LSL_Performance|moved to this page]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Animation Frame Rate ==&lt;br /&gt;
=== The Problem ===&lt;br /&gt;
You may have heard rumors that animations (BVH files) get reduced to 12 FPS when uploaded or played, and there&#039;s no need to make an animation faster than 12 frames per second (FPS). Other rumors say that fast animation frame rates give smoother animations, while other rumors say that you should use the lowest animation frame rate possible. Confused? Here&#039;s some information that might help.&lt;br /&gt;
=== The SL Viewer Interpolates ===&lt;br /&gt;
Part of the confusion is that the term &amp;quot;frame rate&amp;quot; means different things in different contexts. The SL viewer renders frames of the scene, including any animations in the scene, as fast as it can. You can always see how fast the client is rendering frames by looking for the FPS number in the Basic section in the Statistics Bar (Ctrl-Shift-1). The frame rate in an animation file is just there to establish the time scale of the animation information. It&#039;s a different kind of &amp;quot;frame.&amp;quot; So how do the two &amp;quot;frame rates&amp;quot; relate and how does it all work? It&#039;s simple -- when the viewer renders a frame, it determines how far along it is in the animation and interpolates the avatar&#039;s bone rotations based on the nearest two frames in the animation file.&lt;br /&gt;
&lt;br /&gt;
Refer to the following illustration to see how you can demonstrate this interpolation. Using your favorite animation editor, make an animation file consisting of just three or four frames. The first frame is the requisite reference frame, then frame 2 is the first key frame of the animation that defines the start of a movement. Frame 3 is the last frame of the movement. Optionally you can add a frame 4 that is a copy of frame2 to make a loop. Make a large movement between frames 2 and 3, such as arms straight down in frame 2 and straight up in frame 3. Set the animation for 1 frame per second. Upload the short animation file and set the looping parameters if needed so that the animation loops continuously.&lt;br /&gt;
&lt;br /&gt;
With the animation set to one frame per second, the arms should start at the sides, then one second later be raised above the head, then two seconds after the start of the animation, the arms should be back at the sides, then repeat. What happens if the client renders frames faster than one per second? If the viewer didn&#039;t interpolate, you would expect the avatar&#039;s arms to snap suddenly from one position to the other... snap up... snap down... etc. at one-second intervals. But that&#039;s not what happens at all. Regardless of your viewer frame rate, you&#039;ll see your avatar&#039;s hands moving smoothly, up and down, with all the intermediate positions between animation frames interpolated smoothly. To examine the effect in more detail, click Advanced-&amp;gt;Character-&amp;gt;Slow Motion Animations, then you&#039;ll have a whole bunch of interpolated rendered frames to examine.&lt;br /&gt;
&lt;br /&gt;
[[Image:Animation-interpolation.jpg|500px|||Viewer interpolates animation frames]]&lt;br /&gt;
&lt;br /&gt;
=== Asynchronous Frame Rates ===&lt;br /&gt;
In the following timeline example, the top lines represent the frames in an animation file, and the bottom lines show how fast the viewer renders frames and where the rendered frames correspond in time to the animation file. In this illustration, the animation frames are numbered starting at two, because frame one is the reference frame that never gets rendered. When the client is rendering the first frame of the animation (render frame 0 in the illustration), it corresponds to frame 2 in the animation file. In this illustration, the client is running at a fairly constant rate a little more than three times the rate of frames in the animation file:&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
animation frame number        2                         3                         4                         5&lt;br /&gt;
&lt;br /&gt;
animation frames:             K-------------------------K-------------------------K-------------------------K-----&lt;br /&gt;
&lt;br /&gt;
viewer rendered frames:       |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|&lt;br /&gt;
&lt;br /&gt;
viewer frame number:          0       1       2       3       4       5       6       7       8       9      10&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
When the viewer is rendering frame 0, the bone rotations exactly correspond to frame 2 in the animation file. After that, the time when a frame is rendered will almost always fall somewhere between two of the animation frames and the client will interpolate the bone rotations. For example, when the client renders frame 1, the bone rotations will be something between the rotations in frames 2 and 3 of the animation file, but closer to the rotations in frame 2. &lt;br /&gt;
&lt;br /&gt;
If the client is running slow in comparison to the animation file, then some data in the animation file must be skipped:&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
animation frame number        2     3     4     5     6     7     8     9    10    11    12    13    14    15 &lt;br /&gt;
&lt;br /&gt;
animation key frames:         K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K---&lt;br /&gt;
&lt;br /&gt;
viewer rendered frames:       |--------------|---------------|---------------|---------------|---------------|--&lt;br /&gt;
&lt;br /&gt;
viewer frame number:          0              1               2               3               4               5 &lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
In this scenario, when it&#039;s time for the client to render frame 1, the animation information is interpolated from frames 4 and 5 in the animation file, which means that any quick movement defined in frames 3 and 4 of the animation file will have been ignored that time around. If the animation loops, then perhaps on a subsequent loop the frames will align differently and a different set of animation frames will be ignored.&lt;br /&gt;
&lt;br /&gt;
=== Other Considerations ===&lt;br /&gt;
There are additional forces at work you need to be aware of:&lt;br /&gt;
&lt;br /&gt;
1. The higher the animation frame rate, the larger the file that has to be sent from the sim to every client in viewing range of the animation. Smaller is less laggy.&lt;br /&gt;
&lt;br /&gt;
2. When you upload an animation, the viewer silently drops small movements. It does this by comparing each bone&#039;s rotation in two adjacent frames, then dropping any data for bones that don&#039;t move much. The exact algorithm is complicated, but in general the threshold is a few degrees in any axis from one frame to the next. If a bone&#039;s rotation changes from one frame to the next less than 3 or 4 degrees in all axes, it might be dropped. If the movement is more than about 10 degrees in any axis, it&#039;s likely to be retained. If an animation is made with a high frame rate, then the movement from frame to frame will be smaller and more movements will be dropped. If the animation frames are less frequent, then the movement between frames will be larger and more likely to be retained.&lt;br /&gt;
&lt;br /&gt;
3. When the viewer interpolates bone rotations from the animation file, it uses a linear interpolation between the nearest frames, which sometimes looks a bit robotic. A higher animation rate lets you specify movements in finer resolution and make the movement start slow, get faster in the middle of the movement, then slow down when it reaches its maximum extent. &lt;br /&gt;
&lt;br /&gt;
=== Summary ===&lt;br /&gt;
The last two forces mentioned above work against each other, so the challenge for animators is to find a balance. The animation needs to be made with a high enough frame rate to describe the movements the way you want them to appear, yet it needs to be as low a frame rate as possible to avoid having the client discard small movements on upload. It depends on the animation, but a good starting point might be around 10 FPS for the animation. Try a lower animation frame rate if the animation appears ok with fewer frames, relying more on the linear interpolation by the client to smooth it out. Use a higher animation frame rate if the linear interpolation doesn&#039;t look quite right and you need finer resolution to describe more complex or non-linear movements.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Script Memory Limits ==&lt;br /&gt;
Script Memory limits are coming in 2010! Here&#039;s some information to help you prepare;&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Limits_FAQ| FAQ about script memory limits]]&lt;br /&gt;
* [[User:Becky_Pippen/Measure_Script_Memory_Usage|Measuring your script memory usage]]&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Efficiency|Calculating memory needed and memory usage efficiency]]&lt;br /&gt;
* [[User:Becky_Pippen/Script_Memory_Limits| A checklist for scripters -- techniques for reducing memory usage]]&lt;br /&gt;
* [[User:Becky_Pippen/New_LSL_Functions| New LSL functions llGetLinkPrimitiveParams(), llSetLinkPrimitiveParamsFast() and more]]&lt;br /&gt;
* [[User:Becky_Pippen/Hashing| Scripting techniques for hashing data to conserve memory]]&lt;br /&gt;
* [[User:Becky_Pippen/Numeric_Storage| Scripting techniques for storing arbitrary binary data without lists]]&lt;br /&gt;
* [[User:Becky_Pippen/Text_Storage| Scripting techniques for compressing ASCII text in Mono]]&lt;br /&gt;
* [http://lslwiki.net/lslwiki/wakka.php?wakka=MemoryUsage Empirical measurements of memory usage by data type and scope (lslwiki.net)]&lt;br /&gt;
* [[LSL_Script_Memory|Empirical measurements of memory usage by data type and scope (SL wiki)]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Shared Media-on-a-Prim LSL recipes ==&lt;br /&gt;
[[User:Becky_Pippen/Shared_Media_LSL_Recipes|See here]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [http://slurl.com/secondlife/Nevia/128/128/23 The Community of the Nevia Archipelago] [http://www.inkwelle.com Website]&lt;br /&gt;
* [http://jira.secondlife.com/ JIRA Issue Tracker]&lt;br /&gt;
* [https://wiki.secondlife.com/wiki/About_AWG Architecture Working Group]&lt;br /&gt;
* [https://wiki.secondlife.com/wiki/Mono Mono wiki]&lt;br /&gt;
{{skills&lt;br /&gt;
|Builder=*&lt;br /&gt;
|Scripter=*&lt;br /&gt;
|Terraformer=&lt;br /&gt;
|Architect=&lt;br /&gt;
|Scenographer=&lt;br /&gt;
|SLogistician=&lt;br /&gt;
|}}&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Shared_Media_LSL_Recipes&amp;diff=817402</id>
		<title>User:Becky Pippen/Shared Media LSL Recipes</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Shared_Media_LSL_Recipes&amp;diff=817402"/>
		<updated>2010-03-22T22:54:31Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Shared Media LSL Recipes&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==How-to&#039;s with Media-on-a-Prim==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
  MediaWiki issue: If anyone knows how to highlight parts of code inside the &amp;lt;lsl&amp;gt;&lt;br /&gt;
  wrapper, let me know. I resorted to using a style attribute in a &amp;lt;span&amp;gt; element&lt;br /&gt;
  inside a &amp;lt;tt&amp;gt; element to yellow-highlight preformatted monospace text. It would&lt;br /&gt;
  be nicer if we could also get the LSL syntax highlighting, but if we have to choose&lt;br /&gt;
  one or the other, I think the yellow highlighting is more useful and instructive&lt;br /&gt;
  for this particular page than LSL syntax highlighting.&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The examples here assume you&#039;re comfortable with LSL, and you just need a quick reference for the syntax of handling JavaScript in LSL for Shared Media scripting. All the examples on this page are ready to copy-n-paste and drop into a prim — no other setup needed. The key syntax in each example is highlighted. There&#039;s no error handling in these minimalist examples, so add plenty of your own.&lt;br /&gt;
&lt;br /&gt;
===Display plain text — XyText replacement===&lt;br /&gt;
[[Image:MoaP-example_1.png‎|thumb|Plain text]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;integer face = 4;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string message = &amp;quot;Hello World&amp;quot;;&amp;lt;/span&amp;gt;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llSetPrimMediaParams(face,&amp;lt;/span&amp;gt;&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;[PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message]);&amp;lt;/span&amp;gt;&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* An avatar must press the Reload button on the browser to see this rendered in-world. A page will be auto-loaded if you include the parameter &amp;lt;tt&amp;gt;PRIM_MEDIA_AUTO_PLAY, TRUE&amp;lt;/tt&amp;gt;, and if the avatar has auto-load enabled in preferences.&lt;br /&gt;
* There&#039;s no need to add any %-hex-escaping or to use [[llEscapeURL]](); llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, url]) will automatically escape the string for you.&lt;br /&gt;
* This works by feeding a data URI to the on-prim browser. Think of it as serving the contents of the page in the URL itself. For more information, see [[Shared_Media_and_data_URI|here]] and [http://en.wikipedia.org/wiki/Data_URI_scheme here].&lt;br /&gt;
* The in-world browser limits the data URI (or any address) to 1024 bytes after %-hex-escaping and encoded in UTF-8. See below for tricks using external or self-served JavaScript or CSS to leverage the data URI.&lt;br /&gt;
* If the data URI contains just plain text, the prefix &amp;lt;tt&amp;gt;&amp;quot;data:text/html,&amp;quot;&amp;lt;/tt&amp;gt; can be abbreviated &amp;lt;tt&amp;gt;&amp;quot;data:,&amp;quot;&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Force a page reload===&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
[[Image:MoaP-example-refreshpage.gif|thumb|Auto-refresh]]&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string myURL;&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;integer seq = 0; // sequence number for unique URLs&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             myURL = body;&lt;br /&gt;
             llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_AUTO_PLAY, TRUE,&lt;br /&gt;
                  PRIM_MEDIA_CURRENT_URL, myURL]);&lt;br /&gt;
             llSetTimerEvent(5.0);&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             llHTTPResponse(id, 200, &amp;quot;Sim FPS: &amp;quot; + (string)llGetRegionFPS());&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     timer()&lt;br /&gt;
     {&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_CURRENT_URL, myURL + &amp;quot;/?r=&amp;quot; + (string)(++seq)&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
Comments:&lt;br /&gt;
* The page will reload automatically if &amp;lt;tt&amp;gt;PRIM_MEDIA_AUTO_PLAY&amp;lt;/tt&amp;gt; is enabled and if the new &amp;lt;tt&amp;gt;PRIM_MEDIA_CURRENT_URL&amp;lt;/tt&amp;gt; is different. This technique appends a short dummy parameter (like &amp;lt;tt&amp;gt;&amp;quot;/?r=12&amp;quot;&amp;lt;/tt&amp;gt;) to the end of the URL to make it different each time, forcing a reload. The parameter is otherwise ignored.&lt;br /&gt;
&lt;br /&gt;
===Display text with HTML markup===&lt;br /&gt;
[[Image:MoaP-example-markup.png‎‎|thumb|HTML markup]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string message = &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;quot;&amp;amp;lt;i&amp;gt;Hello&amp;amp;lt;/i&amp;gt;&amp;amp;lt;h2&amp;gt;World!&amp;amp;lt;/h2&amp;gt;&amp;quot;&amp;lt;/span&amp;gt;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* The text of the data URI will be %-hex-escaped automatically for you, so there&#039;s no need to use [[llEscapeURL]](). All you need in the data URI is the prefix &amp;lt;tt&amp;gt;data:text/html,&amp;lt;/tt&amp;gt; plus your HTML.&lt;br /&gt;
&lt;br /&gt;
===Change window resolution===&lt;br /&gt;
[[Image:MoaP-example-size.png‎|thumb|Resized web window]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string message = &amp;quot;Hello World&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message,&lt;br /&gt;
                 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_WIDTH_PIXELS, 128&amp;lt;/span&amp;gt;,&lt;br /&gt;
                 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_HEIGHT_PIXELS, 32&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* The browser will add scroll bars if the rendered HTML doesn&#039;t fit within the specified PRIM_MEDIA_* size.&lt;br /&gt;
&lt;br /&gt;
===Display an image — works with animated GIFs too===&lt;br /&gt;
[[Image:MoaP-example-animgif.gif‎|thumb|Image on a prim]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL =&lt;br /&gt;
           &amp;quot;http://upload.wikimedia.org/wikipedia/commons/7/70/Rotating_earth_(small).gif&amp;quot;;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string dataURI = &amp;quot;data:text/html,&amp;amp;lt;object data=&#039;&amp;quot; + imageURL + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/object&amp;gt;&amp;quot;;&amp;lt;/span&amp;gt;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [PRIM_MEDIA_CURRENT_URL, dataURI,&lt;br /&gt;
              PRIM_MEDIA_WIDTH_PIXELS, 256,&lt;br /&gt;
              PRIM_MEDIA_HEIGHT_PIXELS, 256]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* If attempting to display YouTube video full-frame, see [https://blogs.secondlife.com/community/community/tnt/blog/2010/03/16/viewer-2-tip-shared-media-show-youtube-on-a-full-prims-face this discussion].&lt;br /&gt;
* For images, you can also use &amp;lt;tt&amp;gt;&amp;amp;lt;image src= &amp;gt;&amp;lt;/tt&amp;gt; like this:&lt;br /&gt;
 string dataURI=&amp;quot;data:text/html,&amp;lt;img src=&#039;&amp;quot; + imageURL + &amp;quot;&#039;&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
===Display a background image, tiled===&lt;br /&gt;
&lt;br /&gt;
====Method #1 — using CSS in a style element in &amp;lt;head&amp;gt;====&lt;br /&gt;
[[Image:MoaP-example-bgTiled.png‎|thumb|Tiled background image]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL =&lt;br /&gt;
             &amp;quot;http://www.google.com/intl/en_ALL/images/logo.gif&amp;quot;;&lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;amp;lt;head&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;style type=&#039;text/css&#039;&amp;gt;body{background-image:url(\&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;imageURL + &amp;quot;\&amp;quot;);}&amp;amp;lt;/style&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;amp;lt;body&amp;gt;Hello World&amp;amp;lt;/body&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Method #2  — using background attribute in &amp;amp;lt;body&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL = &amp;quot;http://www.google.com/intl/en_ALL/images/logo.gif&amp;quot;;&lt;br /&gt;
         string dataURI = &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;data:text/html,&amp;amp;lt;body background=&#039;&amp;quot; + imageURL + &amp;quot;&#039;&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;Hello World&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Method #3 — using style attribute in &amp;lt;body&amp;gt;====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string imageURL = &amp;quot;http://www.google.com/intl/en_ALL/images/logo.gif&amp;quot;;&lt;br /&gt;
         string dataURI = &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;data:text/html,&amp;amp;lt;body style=&#039;background-image:url(\&amp;quot;&amp;quot; + imageURL + &amp;quot;\&amp;quot;)&#039;&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;Hello World&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
             + &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Text colors — using &amp;amp;lt;font&amp;gt; element===&lt;br /&gt;
[[Image:MoaP-example-font-color.png‎|thumb|Font color]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         string message = &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;font color=&#039;Red&#039;&amp;gt;Hello World&amp;amp;lt;/font&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;PRIM_MEDIA_CURRENT_URL, &amp;quot;data:text/html,&amp;quot; + message&amp;lt;/span&amp;gt;]);&lt;br /&gt;
     }&lt;br /&gt;
 }  &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* You can also specify color and other CSS rules in a style attribute in another element. For example, you can replace message in the example above with:&lt;br /&gt;
&lt;br /&gt;
 string message = &amp;quot;&amp;amp;lt;h2 style=&#039;color:#FF0000&#039;&amp;gt;Hello World&amp;amp;lt;/h2&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
===Link to external CSS file (forms example)===&lt;br /&gt;
[[Image:MoaP-example-externalCSS.png‎|thumb|External CSS file]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         integer face = 4;&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string externalCSS =&amp;lt;/span&amp;gt;&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;quot;http://www.google.com/css/modules/buttons/g-button-chocobo.css&amp;quot;;&amp;lt;/span&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;lt;head&amp;gt;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;lt;link href=&#039;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
                 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;externalCSS + &amp;quot;&#039; rel=&#039;stylesheet&#039; type=&#039;text/css&#039; /&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;quot; +&lt;br /&gt;
                 &amp;quot;&amp;amp;lt;form&amp;gt;&amp;amp;lt;input type=&#039;button&#039; value=&#039;Click Me&#039; class=&#039;g-button&#039; /&amp;gt;&amp;amp;lt;/form&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
                 [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 }  &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* This is one way to work around the 1024-byte limitation of data URIs. The external CSS can be as large as the browser permits. Also see the examples below for putting JavaScript in an external file.&lt;br /&gt;
* The element &amp;lt;tt&amp;gt;&amp;lt;head&amp;gt;&amp;lt;/tt&amp;gt; and the attribute &amp;lt;tt&amp;gt;type=&#039;text/css&#039;&amp;lt;/tt&amp;gt; may be omitted to make the data URI a few bytes shorter.&lt;br /&gt;
&lt;br /&gt;
===Calling external JavaScript functions (JQuery example)===&lt;br /&gt;
[[Image:MoaP-example-JQuery.gif‎|thumb|Calling external JQuery]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string externalJavascript =&lt;br /&gt;
     &amp;quot;http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js&amp;quot;;&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;amp;lt;head&amp;gt;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;script src=&#039;&amp;quot; + externalJavascript + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;lt;/span&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;amp;lt;button&amp;gt;Toggle&amp;amp;lt;/button&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;amp;lt;p&amp;gt;Hello&amp;amp;lt;br /&amp;gt;World&amp;amp;lt;/p&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;script&amp;gt;$(&#039;button&#039;).click(function () &amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;{$(&#039;p&#039;).slideToggle(&#039;slow&#039;);});&amp;amp;lt;/script&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         llSetPrimMediaParams(face,&lt;br /&gt;
             [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* This is a technique for working around the 1024-byte limit for data URIs. When you can&#039;t fit all the JavaScript you need into one data URI, just move some JavaScript functions into an external file and give the browser a data URI that references the external file with a URL. In this example above, the JavaScript for [http://jquery.com/ JQuery] lives in an external file on Google&#039;s servers. You just need enough bytes left over in the data URI to put a JavaScript function call with its parameters inside a &amp;lt;tt&amp;gt;&amp;amp;lt;script&amp;gt;&amp;lt;/tt&amp;gt; element.&lt;br /&gt;
* A data URI can be used as a springboard to generate arbitrary complex web pages by making use of these elements:&lt;br /&gt;
** a reference to external CSS with &amp;lt;tt&amp;gt;&amp;amp;lt;link href= &amp;lt;/tt&amp;gt;, &lt;br /&gt;
** a reference to external JavaScript with &amp;lt;tt&amp;gt;&amp;amp;lt;script src=&amp;lt;/tt&amp;gt;, and&lt;br /&gt;
** a function call with parameters to the external JavaScript using &amp;lt;tt&amp;gt;&amp;amp;lt;script&amp;gt;someFunction(args);&amp;amp;lt;/script&amp;gt;&amp;lt;/tt&amp;gt;. (In the JQuery example above, the name of the external function is &amp;lt;tt&amp;gt;&amp;quot;$&amp;quot;&amp;lt;/tt&amp;gt;.)&lt;br /&gt;
* See the next example for more of this same technique.&lt;br /&gt;
&lt;br /&gt;
===Self-served JavaScript===&lt;br /&gt;
====Example #1 — using &amp;amp;lt;script src= and .innerHTML= ====&lt;br /&gt;
[[Image:MoaP-example-refresh-clock.gif‎|thumb|Self-served auto-refreshed pages]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string selfServedJavascriptURL;&lt;br /&gt;
 &lt;br /&gt;
 // This can be up to 2KBytes after %-hex-escaping:&lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;string servedJavascript =&amp;lt;/span&amp;gt; &amp;quot;&lt;br /&gt;
 function checklength(i){if (i&amp;lt;10) {i=&#039;0&#039;+i;} return i;}      &lt;br /&gt;
 &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;function clock()&amp;lt;/span&amp;gt;{ &lt;br /&gt;
  var now = new Date();  &lt;br /&gt;
  var hours = checklength(now.getHours());  &lt;br /&gt;
  var minutes = checklength(now.getMinutes());  &lt;br /&gt;
  var seconds = checklength(now.getSeconds());  &lt;br /&gt;
  var format = 1; //0=24 hour format, 1=12 hour format   &lt;br /&gt;
  var time;  &lt;br /&gt;
  if (format == 1) { &lt;br /&gt;
   if (hours &amp;gt;= 12) {  &lt;br /&gt;
    if (hours ==12 ) { hours = 12;&lt;br /&gt;
    } else { hours = hours-12; } &lt;br /&gt;
    time=hours+&#039;:&#039;+minutes+&#039;:&#039;+seconds+&#039; PM&#039;;   &lt;br /&gt;
   } else if(hours &amp;lt; 12) { &lt;br /&gt;
    if (hours ==0) {hours=12;}   &lt;br /&gt;
    time=hours+&#039;:&#039;+minutes+&#039;:&#039;+seconds+&#039; AM&#039;;   &lt;br /&gt;
   }   &lt;br /&gt;
  }  &lt;br /&gt;
  if (format == 0) {time= hours+&#039;:&#039;+minutes+&#039;:&#039;+seconds;}   &lt;br /&gt;
  &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;document.getElementById(&#039;clock&#039;).innerHTML=time;&amp;lt;/span&amp;gt;&lt;br /&gt;
  setTimeout(&#039;clock();&#039;, 500); &lt;br /&gt;
 } &amp;quot;; // yes, that&#039;s one long string.&lt;br /&gt;
 &lt;br /&gt;
 displayPage()&lt;br /&gt;
 {&lt;br /&gt;
     string dataURI = &amp;quot;data:text/html,&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;script src=&#039;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;selfServedJavascriptURL + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
         &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;div id=&#039;clock&#039;&amp;gt;&amp;amp;lt;script&amp;gt;clock();&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/div&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
     llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, dataURI]);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;selfServedJavascriptURL = body;&amp;lt;/span&amp;gt;&lt;br /&gt;
             displayPage();&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPResponse(id, 200, servedJavascript);&amp;lt;/span&amp;gt;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* This is a technique for trading the 1024-byte limit of data URIs for the 2048-byte-per-page limit that the script can serve itself. As in the previous example, when you can&#039;t fit all the JavaScript into one data URI, just move the JavaScript into an external file and reference the file with &amp;lt;tt&amp;gt;&amp;amp;lt;script src=URL&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;lt;/tt&amp;gt;. When the URL refers to the script&#039;s own HTTP-in URL, then the page served can contain up to 2K bytes, so you get at least that many bytes plus whatever logic you can fit into the rest of the data URI. If you move that JavaScript to an external web server, the size of the external JavaScript file can be as large as the client web browser can render.&lt;br /&gt;
* Your script can serve an arbitrary number of different pages through one HTTP-in URL by appending a path or parameter to the URL.&lt;br /&gt;
* This works by assigning a string containing HTML to the &amp;lt;tt&amp;gt;.innerHTML&amp;lt;/tt&amp;gt; contents of the &amp;lt;tt&amp;gt;&amp;amp;lt;div&amp;gt;&amp;lt;/tt&amp;gt; element with id &amp;quot;clock&amp;quot;. This achieves something similar to the [http://talirosca.wikidot.com/bootstrapping-html-on-a-prim technique published by Tali Rosca] and mentioned [http://wiki.secondlife.com/wiki/User:Kelly_Linden/lsl_hacks#DHTML_.2F_Javascript_.28Tali_Rosca.29 here]. In those techniques, a string is constructed containing an &amp;lt;tt&amp;gt;&amp;amp;lt;a href=&amp;lt;/tt&amp;gt; tag and an &amp;lt;tt&amp;gt;href=&amp;lt;/tt&amp;gt; that points to the script&#039;s HTTP-in URL. That string then replaces the &amp;lt;tt&amp;gt;&amp;amp;lt;body&amp;gt;&amp;lt;/tt&amp;gt; element using:&lt;br /&gt;
 &amp;lt;tt&amp;gt;document.getElementsByTagName(&#039;body&#039;)[0].innerHTML = &amp;lt;i&amp;gt;new-content&amp;lt;/i&amp;gt;&amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Example #2 — using &amp;lt;body onload= (lag graph example)====&lt;br /&gt;
[[Image:MoaP-example-laggraph.gif‎|thumb|Refreshed pages]]&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 integer face = 4;&lt;br /&gt;
 string jsURL; // where to fetch external JavaScript&lt;br /&gt;
 list numbers;&lt;br /&gt;
 integer numSamples = 50;&lt;br /&gt;
 &lt;br /&gt;
 // This is self-served in this example, but can be&lt;br /&gt;
 // moved to an external server:&lt;br /&gt;
 //&lt;br /&gt;
 string externalJavascript()&lt;br /&gt;
 {&lt;br /&gt;
     return&lt;br /&gt;
         &amp;quot;function bar(widthPct,heightPix) {&amp;quot; +&lt;br /&gt;
         &amp;quot; document.writeln(\&amp;quot;&amp;amp;lt;hr style=&#039;padding:0;margin:0;&amp;quot; +&lt;br /&gt;
         &amp;quot;  margin-top:-1px;text-align:left;align:left;border=0;&amp;quot; +&lt;br /&gt;
         &amp;quot;  width:\&amp;quot;+widthPct+\&amp;quot;%;height:\&amp;quot;+heightPix+\&amp;quot;px;&amp;quot; +&lt;br /&gt;
         &amp;quot;  background-color:#c22;color:#c22;&#039;&amp;gt;\&amp;quot;);}&amp;quot; +&lt;br /&gt;
         &amp;quot; function graphBars(arr){for(var i=0;i&amp;amp;lt;arr.length;++i)&amp;quot; +&lt;br /&gt;
         &amp;quot;  {bar(arr[i],18);}}&amp;quot;;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llClearPrimMedia(face);&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
         integer i = numSamples;&lt;br /&gt;
         while (--i &amp;gt;= 0) {&lt;br /&gt;
             numbers += 0;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     timer()&lt;br /&gt;
     {&lt;br /&gt;
         numbers = llList2List(numbers, 1, -1) + [(integer)(6.0 * (45.0 - llGetRegionFPS()))];&lt;br /&gt;
 &lt;br /&gt;
         // The dataURI loads external JavaScript functions and calls one with parameters:&lt;br /&gt;
 &lt;br /&gt;
         string dataURI = &amp;quot;data:text/html,&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;amp;lt;head&amp;gt;&amp;amp;lt;script src=&#039;&amp;quot; + jsURL + &amp;quot;&#039;&amp;gt;&amp;amp;lt;/script&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;lt;/span&amp;gt;&amp;quot; +&lt;br /&gt;
             &amp;quot;&amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;&amp;lt;body onload=\&amp;quot;graphBars([&amp;quot; + llList2CSV(numbers) + &amp;quot;]);\&amp;quot;&amp;gt;&amp;amp;lt;/body&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
         llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, dataURI, PRIM_MEDIA_AUTO_PLAY, TRUE]);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             jsURL = body; // self-serve the JavaScript&lt;br /&gt;
             llSetTimerEvent(1.0);&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             llHTTPResponse(id, 200, externalJavascript());&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* Like the previous example, this is another variation of how to structure a data URI to serve as a springboard for arbitrarily large pages. In this example, the data URI first uses a &amp;lt;tt&amp;gt;&amp;amp;lt;script src=...&amp;gt;&amp;lt;/tt&amp;gt; tag to read a file of external JavaScript functions in the &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;lt;/tt&amp;gt; element, and then the &amp;lt;tt&amp;gt;onload=&amp;lt;/tt&amp;gt; attribute in &amp;lt;tt&amp;gt;&amp;amp;lt;body&amp;gt;&amp;lt;/tt&amp;gt; triggers a call to one of the functions which executes a loop creating a bunch of &amp;lt;tt&amp;gt;&amp;amp;lt;hr&amp;gt;&amp;lt;/tt&amp;gt; elements that form the bar chart. The size of HTML generated by the JavaScript can be as large as the browser permits.&lt;br /&gt;
* In this example, &amp;lt;tt&amp;gt;jsURL&amp;lt;/tt&amp;gt; is set to the script&#039;s own HTTP-in URL so that the example can be self-contained, but you can point &amp;lt;tt&amp;gt;jsURL&amp;lt;/tt&amp;gt; to an external URL as well. If served by an external server, the JavaScript read in &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;lt;/tt&amp;gt; can be as large as the browser permits.&lt;br /&gt;
* &amp;lt;tt&amp;gt;&amp;amp;lt;head&amp;gt;&amp;amp;lt;/head&amp;gt;&amp;lt;/tt&amp;gt; can be omitted in the data URI surrounding the &amp;lt;tt&amp;gt;&amp;amp;lt;script src=&amp;lt;/tt&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
===Make TinyURLs by script===&lt;br /&gt;
&amp;lt;tt&amp;gt;&lt;br /&gt;
 string myTinyURL;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         llRequestURL();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_request(key id, string method, string body)&lt;br /&gt;
     {&lt;br /&gt;
         if (method == URL_REQUEST_GRANTED) {&lt;br /&gt;
             // Send our full URL to tinyurl.com for conversion&lt;br /&gt;
             // The answer will come back in http_response()&lt;br /&gt;
             &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;llHTTPRequest(&amp;quot;http://tinyurl.com/api-create.php?url=&amp;quot; + body, [], &amp;quot;&amp;quot;);&amp;lt;/span&amp;gt;&lt;br /&gt;
         } else if (method == &amp;quot;GET&amp;quot;) {&lt;br /&gt;
             llHTTPResponse(id, 200, &amp;quot;Hello Real World from the Virtual World&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     http_response(key req, integer stat, list met, string body)&lt;br /&gt;
     {&lt;br /&gt;
         &amp;lt;span style=&amp;quot;background-color:#F8F8C8&amp;quot;&amp;gt;myTinyURL = body;&amp;lt;/span&amp;gt;&lt;br /&gt;
         llOwnerSay(&amp;quot;My HTTP-in TinyURL is: &amp;quot; + myTinyURL + &amp;quot; , Click Me!&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/tt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Comments:&lt;br /&gt;
* Any URL or data URI can be mapped to a TinyURL. The example above reduces an assigned HTTP-in URL, which is a long one such as:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://sim4605.agni.lindenlab.com:12046/cap/2b9f06f7-431e-5b0f-9271-2d03bd15370b&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
into a TinyURL this size:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://tinyurl.com/y9etul3&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Development hints===&lt;br /&gt;
# You can develop your HTML and JavaScript outside of Second Life by putting your JavaScript incantations in data URIs that you feed to any web browser. For the most compatible browsers, use Safari or Google Chrome, or any other that uses the [[QtWebKit|WebKit]] HTML rendering engine. Personally I like the open source [http://code.google.com/p/arora/ Arora web browser] for simulating Media-on-a-Prim: it&#039;s based on WebKit and has a handy inspect feature for browsing the HTML and debugging the JavaScript that you&#039;re developing.&lt;br /&gt;
# If you don&#039;t have direct access to a web server while developing this stuff, then consider installing a local copy of [http://www.apache.org/ Apache web server] on your computer. You can restrict it to local access. Then you can refer to files on your own computer with URLs that start with &amp;lt;tt&amp;gt;&amp;lt;nowiki&amp;gt;http://localhost/&amp;lt;/nowiki&amp;gt;....&amp;lt;/tt&amp;gt; That lets you, for example, edit &amp;lt;tt&amp;gt;myfile.js&amp;lt;/tt&amp;gt; on your own computer while working on a data URI that contains &amp;lt;tt&amp;gt;&amp;amp;lt;script src=&amp;quot;&amp;lt;nowiki&amp;gt;http://localhost/myfile.js&amp;lt;/nowiki&amp;gt;&amp;quot;&amp;gt;&amp;lt;/tt&amp;gt;. Then when &amp;lt;tt&amp;gt;myfile.js&amp;lt;/tt&amp;gt; is working, you can put the debugged JavaScript into an LSL script that will serve it through its HTTP-in port. That&#039;s easier than debugging all that in-world.&lt;br /&gt;
&lt;br /&gt;
===References and sources===&lt;br /&gt;
* [[User:Kelly_Linden/lsl_hacks|Kelly Linden&#039;s hacks]]&lt;br /&gt;
* [http://talirosca.wikidot.com/bootstrapping-html-on-a-prim Tali Rosca&#039;s techniques]&lt;br /&gt;
* [https://blogs.secondlife.com/message/115303 Notecard Text on a Prim - Demo]&lt;br /&gt;
* [[llSetPrimMediaParams]]()&lt;br /&gt;
* {{JIRA|SVC-3427}}&lt;br /&gt;
* [https://blogs.secondlife.com/message/124144 Ajax in Shared Media #1]&lt;br /&gt;
* [https://blogs.secondlife.com/thread/11698 Ajax in Shared Media #2]&lt;br /&gt;
* [https://blogs.secondlife.com/thread/12727 Ajax in Shared Media #3]&lt;br /&gt;
* [https://blogs.secondlife.com/thread/13455 SVG (Scalable Vector Graphics) in Shared Media]&lt;br /&gt;
* [[Shared_Media_and_data_URI|Shared_Media_and_data_URI]]&lt;br /&gt;
* [https://blogs.secondlife.com/community/community/tnt/blog/2010/03/17/viewer-2-tip-shared-media-add-custom-content-with-data-uri-no-webpage-upload-needed Shared Media: Add custom content with data: URI]&lt;br /&gt;
* [https://blogs.secondlife.com/message/119704 &amp;quot;Shared Media&amp;quot; - A few facts]&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=File:MoaP-example_1.png&amp;diff=817392</id>
		<title>File:MoaP-example 1.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=File:MoaP-example_1.png&amp;diff=817392"/>
		<updated>2010-03-22T22:53:26Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Media-on-a-Prim example of plain text&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Media-on-a-Prim example of plain text&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-refreshpage.gif&amp;diff=817382</id>
		<title>File:MoaP-example-refreshpage.gif</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-refreshpage.gif&amp;diff=817382"/>
		<updated>2010-03-22T22:47:18Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Media-on-a-Prim example of auto page reload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Media-on-a-Prim example of auto page reload&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-markup.png&amp;diff=817362</id>
		<title>File:MoaP-example-markup.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-markup.png&amp;diff=817362"/>
		<updated>2010-03-22T22:46:30Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Media-on-a-Prim example of HTML markup&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Media-on-a-Prim example of HTML markup&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-size.png&amp;diff=817352</id>
		<title>File:MoaP-example-size.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-size.png&amp;diff=817352"/>
		<updated>2010-03-22T22:45:37Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Media-on-a-Prim example of scroll bars&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Media-on-a-Prim example of scroll bars&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-animgif.gif&amp;diff=817342</id>
		<title>File:MoaP-example-animgif.gif</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-animgif.gif&amp;diff=817342"/>
		<updated>2010-03-22T22:23:49Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Media-on-a-Prim example of animated GIF on a prim face&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Media-on-a-Prim example of animated GIF on a prim face&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-bgTiled.png&amp;diff=817332</id>
		<title>File:MoaP-example-bgTiled.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-bgTiled.png&amp;diff=817332"/>
		<updated>2010-03-22T22:21:00Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Media-on-a-Prim example of tiled background image&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Media-on-a-Prim example of tiled background image&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-font-color.png&amp;diff=817322</id>
		<title>File:MoaP-example-font-color.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-font-color.png&amp;diff=817322"/>
		<updated>2010-03-22T22:17:52Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Media-on-a-Prim example of font color&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Media-on-a-Prim example of font color&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-externalCSS.png&amp;diff=817312</id>
		<title>File:MoaP-example-externalCSS.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-externalCSS.png&amp;diff=817312"/>
		<updated>2010-03-22T22:17:16Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Media-on-a-Prim example of referring to external CSS&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Media-on-a-Prim example of referring to external CSS&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-JQuery.gif&amp;diff=817302</id>
		<title>File:MoaP-example-JQuery.gif</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-JQuery.gif&amp;diff=817302"/>
		<updated>2010-03-22T22:16:04Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Media-on-a-Prim example of calling external JQuery function&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Media-on-a-Prim example of calling external JQuery function&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-refresh-clock.gif&amp;diff=817272</id>
		<title>File:MoaP-example-refresh-clock.gif</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-refresh-clock.gif&amp;diff=817272"/>
		<updated>2010-03-22T22:14:58Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Media-on-a-Prim example of clock - page reloads&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Media-on-a-Prim example of clock - page reloads&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-laggraph.gif&amp;diff=817242</id>
		<title>File:MoaP-example-laggraph.gif</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=File:MoaP-example-laggraph.gif&amp;diff=817242"/>
		<updated>2010-03-22T22:10:45Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Media-on-a-Prim example of graph page reloading&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Media-on-a-Prim example of graph page reloading&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Memory_Limits_FAQ&amp;diff=816902</id>
		<title>User:Becky Pippen/Memory Limits FAQ</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Memory_Limits_FAQ&amp;diff=816902"/>
		<updated>2010-03-22T08:15:37Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Add link to Jack&amp;#039;s official blog announcement.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== FAQ - Script Memory Limits ==&lt;br /&gt;
&lt;br /&gt;
For the most official announcement so far, see [https://blogs.secondlife.com/community/land/blog/2010/03/19/the-first-step-toward-script-limits here].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Why do we need script memory limits?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; In some regions, the simulator uses so much script memory that it has to swap virtual memory to disk. That causes major lag, and lag is among the top complaints about Second Life.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; How can I tell if I&#039;m a heavy script user?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Starting with server version 1.38, we&#039;ll have new ways built into the sim and viewer to see our individual script memory usage, similar to how estate managers can see top scripts in a region. Also scripters can use the function [[LlGetFreeMemory|llGetFreeMemory()]] to get an approximate idea of memory usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will this break content?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Yes, some. The limits are specifically designed to limit the worst 5% of memory consumers so that the remaining 95% can continue to run freely without causing the simulator to swap virtual memory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; When will all this happen?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Script memory reporting and the new memory-efficient LSL functions are [https://blogs.secondlife.com/community/technology/blog/2010/03/05/server-138-beta-now-open available for testing on the Preview Grid] using server version 1.38 and beta 2.0 of the SL viewer. Server version 1.40 or later will have &amp;quot;Small Scripts&amp;quot; capability (see below). Memory limits will not be enforced until later in 2010 after these features have been deployed on the main grid for some time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will the limits be per avatar or per region?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; There will be limits on avatar, parcel, and region in a way that parallels [[LSL_http_server#Resource_Limitations|how URL resources are managed]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will Linden Lab pay for all the scripted products we bought that will no longer work?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Most products are reasonably scripted and will continue to work just fine. The only problematic products will be unusual ones that are scripted in ways that use unreasonable amounts of RAM. Try to get the product creator to make a new version that uses less memory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will we see a reduction in lag across the grid?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; It won&#039;t have a major impact on most regions. However, it will make a huge improvement on the 5% of the regions that use so much script memory that the server has to swap virtual memory to disk. Theoretically it might also somewhat improve performance on any other simulators running on the same server host using the same disk resources.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; How much RAM are we talking about?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; In the 95% of the regions that run smoothly, the simulator uses less than 800MB of RAM for everything except scripts, and up to 300MB for all the scripts in the region, and that&#039;s ok.  In the worst 5% of the regions, simulators use up to 2GB of RAM on the server, and that&#039;s not ok.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Why not just add RAM to the servers? RAM is cheap.&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Several thousand Xeon server boards at, say, US$100 each to upgrade, comes out to a bunch of money, even for Linden Lab.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Who will be affected?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; All scripts, old and new, will be subject to the new memory limits. When planning for the memory limits, [http://wiki.secondlife.com/wiki/User:Babbage_Linden/Office_Hours/2009_11_25 Babbage Linden said], &amp;quot;[2009/11/25 3:43]  Babbage Linden: ideally we&#039;d like to have 95+% of people use scripts as they currently do while still having 95+% of simulators running without swapping&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What&#039;s the difference between &amp;quot;Small Scripts Project,&amp;quot; &amp;quot;Big Scripts Project,&amp;quot; and &amp;quot;Efficient Scripts Project?&amp;quot;&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; The &#039;&#039;Efficient Scripts Project&#039;&#039; is the overall effort to reduce the memory consumption of Mono-based scripts, including limits on memory usage. The &#039;&#039;Small Scripts Project&#039;&#039; and &#039;&#039;Big Scripts Project&#039;&#039; both refer to a refinement where each script can request the specific amount of memory it anticipates using instead of being charged with a constant 16KB for LSL or 64KB for Mono scripts. So, for example, a simple Mono sit script might request only 3KB while another data-intensive Mono script might request several megabytes. Since the average Mono script in SL needs only about 9KB, this could help reduce overall script memory usage in a typical region. In [http://wiki.secondlife.com/wiki/User:Babbage_Linden/Office_Hours/2010_03_03 Office Hours], Babbage Linden said, &amp;quot;I would like to ship script usage, then small scripts, then script limits, then big scripts... so it would be nice to have mono scripts be able to reserve a lower memory amount before script limits enforcement happens.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What&#039;s the &amp;quot;LSL Penalty?&amp;quot;&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; At one time, LL considered charging LSL scripts with more memory than they actually consume as an incentive for scripters to use Mono. That idea has been dropped, and the current plan is to charge all scripts with the actual memory they reserve — currently 16KB for LSL, and 64KB for Mono scripts. In the future after the Small Scripts and Big Scripts Project have been implemented, Mono scripts can be charged for only the amount of memory they reserve, which could be more or less than 64KB. Discussion of this can be found in the [http://lists.secondlife.com/pipermail/opensource-dev/ opensource-dev] mailing list.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What can I do as a content creator to prepare?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Be kind to your customers and give them product updates using the [[User:Becky_Pippen/New_LSL_Functions| new efficient LSL functions]] when they become available. Learn more about how to [[User:Becky_Pippen/Measure_Script_Memory_Usage|measure memory usage]] and the [[User:Becky_Pippen/Script_Memory_Limits|scripting techniques for reducing memory]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What can I do as a customer?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; 95% of scripts are just fine and you don&#039;t need to do anything. If you are wearing hair, shoes, or other attachments with a script in every prim, then you can contact the creator and ask if there is a product update that uses the new LSL memory-efficient functions.&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/New_LSL_Functions&amp;diff=801052</id>
		<title>User:Becky Pippen/New LSL Functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/New_LSL_Functions&amp;diff=801052"/>
		<updated>2010-03-13T00:16:42Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Add link to Maestro&amp;#039;s Linkset_resizer&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== New LSL Functions ==&lt;br /&gt;
&lt;br /&gt;
The information in this section is preliminary and predictive, based on information given by&lt;br /&gt;
[[Babbage_Linden|Babbage Linden at his Office Hours]] and in [https://blogs.secondlife.com/community/technology/blog/2010/03/05/server-138-beta-now-open this SL Blog announcement].&lt;br /&gt;
&lt;br /&gt;
To help reduce the number of scripts needed for certain common operations, we will have these new&lt;br /&gt;
LSL functions in server version 1.38:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; list llGetLinkPrimitiveParams(integer linknumber, list params)&lt;br /&gt;
 &lt;br /&gt;
      llSetLinkPrimitiveParamsFast(integer link, list params)&lt;br /&gt;
 &lt;br /&gt;
      llLinkParticleSystem(integer link, list rules)&lt;br /&gt;
 &lt;br /&gt;
      llSetLinkTextureAnim(integer mode, integer face, integer sizex, integer sizey, float start, float length, float rate)&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will also get [[PRIM_TEXT]] for getting and setting hover text with [[llSetLinkPrimitiveParams]]().&lt;br /&gt;
The parameter list will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; PRIM_TEXT, string msg, vector color, float alpha &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===The deal with resizer scripts===&lt;br /&gt;
&lt;br /&gt;
Some content creators prefer to distribute their objects no-mod, yet still allow customers to resize or&lt;br /&gt;
retexture the linkset. Scripts can do that in a no-mod linkset, but the obvious solution using&lt;br /&gt;
[[llSetLinkPrimitiveParams]]() doesn&#039;t work well because of the 0.2-second delay built into the function.&lt;br /&gt;
To work around the delay, we have had to put a script in every child prim that can respond immediately&lt;br /&gt;
to a message from the root prim. When these functions become available, we will be able to do this kind of thing with a single script.&lt;br /&gt;
&lt;br /&gt;
===Fixing existing scripts===&lt;br /&gt;
&lt;br /&gt;
To fix such a script, locate the call to [[llMessageLinked]]() in the master script where it sends the link&lt;br /&gt;
message to the individual prims. It might look something vaguely like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llMessageLinked(LINK_whatever, command_resize, (string)newSize, NULL_KEY);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Replace that with a loop that applies whatever changes you need to each prim using&lt;br /&gt;
[[llSetLinkPrimitiveParamsFast]](). The prims have link numbers from 1 through [[llGetNumberOfPrims]](),&lt;br /&gt;
so you can make an efficient loop as shown here; just add any additional processing you&lt;br /&gt;
need for each prim inside the loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; applyChildParams(list ruleSet)&lt;br /&gt;
 {&lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum, ruleSet);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, to use this function to set the color and alpha of all the prims, build the parameter&lt;br /&gt;
list and pass it to applyChildParams() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;applyChildParams([PRIM_COLOR, ALL_SIDES, &amp;lt;0.9, 0.8, 0.2&amp;gt;, 1.0]);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
However, when you only need to apply a constant rule set like this to every prim in the linkset,&lt;br /&gt;
then the loop above can be simplified to a single statement;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llSetLinkPrimitiveParamsFast(LINK_SET, ruleSet);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or use [[LINK_ALL_OTHERS]] or [[LINK_ALL_CHILDREN]] instead of [[LINK_SET]] as appropriate.&lt;br /&gt;
&lt;br /&gt;
For another example, you can make a resizer script apply relative instead of absolute size changes by&lt;br /&gt;
using the new function [[llGetLinkPrimitiveParams]](). The script can read each child&#039;s current size,&lt;br /&gt;
then apply a resize factor and use [[llSetLinkPrimitiveParamsFast]]() to set a new size. This can help&lt;br /&gt;
make it easy for the script to do the right thing even if reset and all its variables get reinitialized. The&lt;br /&gt;
following function will resize all the prims (including the root itself) by a factor:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; rescaleLinksetByFactor(float factor)&lt;br /&gt;
 {&lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list params = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE]);&lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum,&lt;br /&gt;
             [PRIM_SIZE, factor * llList2Vector(params, 0)]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But what if the function above encounters a prim that, after resized, would have a dimension outside&lt;br /&gt;
the legal range of 0.01m to 10.0m? That could have unfortunate consequences, so&lt;br /&gt;
here is a function that resizes all the prims by a specified factor but only if all the resulting&lt;br /&gt;
dimensions in all the prims will all be in the legal range of 0.01m to 10.0m. If any dimension in&lt;br /&gt;
any prim would go out of those bounds, the function will return FALSE without resizing anything.&lt;br /&gt;
This example also includes repositioning the child prims within the linkset. Change or add whatever&lt;br /&gt;
additional parameters are needed for your particular application.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // This is not a complete resizer solution; it&#039;s just a demo of&lt;br /&gt;
 // how to call the new LSL functions.&lt;br /&gt;
 &lt;br /&gt;
 // Set the minimum and maximum size allowed for any prim in the linkset:&lt;br /&gt;
 //&lt;br /&gt;
 float minPrimSize = 0.01;&lt;br /&gt;
 float maxPrimSize = 10.0;&lt;br /&gt;
 &lt;br /&gt;
 // Resize all the prims in the linkset. For example, to resize all&lt;br /&gt;
 // the prims to 1% smaller, call this function with factor=0.99&lt;br /&gt;
 // Returns TRUE if successful, or FALSE if resizing would have&lt;br /&gt;
 // set a prim size out of bounds.&lt;br /&gt;
 //&lt;br /&gt;
 integer resizeLinksetWithBounds(float factor)&lt;br /&gt;
 {&lt;br /&gt;
     // Test to see if it&#039;s safe to change all the prim sizes:&lt;br /&gt;
 &lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list params = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE]);&lt;br /&gt;
         vector sz = factor * llList2Vector(params, 0);&lt;br /&gt;
         if (sz.x &amp;lt; minPrimSize || sz.x &amp;gt; maxPrimSize ||&lt;br /&gt;
             sz.y &amp;lt; minPrimSize || sz.y &amp;gt; maxPrimSize ||&lt;br /&gt;
             sz.z &amp;lt; minPrimSize || sz.z &amp;gt; maxPrimSize) {&lt;br /&gt;
                 return FALSE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     // We&#039;ve determined that we can safely change all the sizes, so&lt;br /&gt;
     // let&#039;s do so now.&lt;br /&gt;
 &lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list oldParams = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE, PRIM_POSITION]);&lt;br /&gt;
 &lt;br /&gt;
         list newParams = [ PRIM_SIZE, factor * llList2Vector(oldParams, 0) ];&lt;br /&gt;
         if (linkNum &amp;gt; 1) {&lt;br /&gt;
             vector linkPos = (vector)llList2String(oldParams, 1);&lt;br /&gt;
             vector localPos = (linkPos - llGetRootPosition()) / llGetRootRotation();&lt;br /&gt;
             newParams += [ PRIM_POSITION, factor * localPos ];&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum, newParams);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return TRUE;&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&#039;s a silly example of how it&#039;s used:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Example of how to call resizeLinksetWithBounds()&lt;br /&gt;
 //&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         float factor = llPow(llFrand(2.0), 2.0); // random factor for demo&lt;br /&gt;
         if (resizeLinksetWithBounds(factor)) {&lt;br /&gt;
             llOwnerSay(&amp;quot;Resized successfully by factor = &amp;quot; + (string)factor);&lt;br /&gt;
         } else {&lt;br /&gt;
             llOwnerSay(&amp;quot;Oops, can&#039;t resize by factor &amp;quot; + (string)factor);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
(Acknowledgements: Thanks to Keith Reinard for testing and improving the example above.)&lt;br /&gt;
&lt;br /&gt;
Also see Maestro Linden&#039;s newly posted [[Linkset_resizer|linkset resizer]].&lt;br /&gt;
&lt;br /&gt;
===Resizer challenges===&lt;br /&gt;
&lt;br /&gt;
A complete resizer solution is outside the scope of this article, but might need to consider such things as:&lt;br /&gt;
* You might need a way to reset all the prims to a known size, position, and orientation in case a resize loop breaks before successfully resizing all the prims.&lt;br /&gt;
* The example above might give unexpected results if executed while the object is in motion. For example, there is no way to guarantee that the functions llGetLinkPrimitiveParams() and llGetRootPosition() will be evaluated during the same simulator frame, so the local offset of a child prim could be computed incorrectly as the difference of the root position in one frame and the child position in another frame.&lt;br /&gt;
* Roundoff errors could accumulate if proportional scaling is done &#039;&#039;many&#039;&#039; times. (See [[User_talk:Becky_Pippen/New_LSL_Functions|the Discussion page]].)&lt;br /&gt;
* It&#039;s easy to scale a linkset so that prims get moved beyond their [[Linkability_Rules|linkability limits]], and no easy way for a script to test for this in advance. For more information, see the discussions at {{JIRA|SVC-5328}} and {{JIRA|SVC-5166}}.&lt;br /&gt;
&lt;br /&gt;
===Long range plans===&lt;br /&gt;
&lt;br /&gt;
The new LSL features described above are expected to appear in server version 1.38. Babbage Linden also mentioned a few additional LSL features he would like to implement sometime after 1.38, but with no guarantees if or when:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; PRIM_NAME, PRIM_DESC  // for llGet/SetPrimitiveParams()&lt;br /&gt;
 llAvatarOnLinkSitTarget()&lt;br /&gt;
 llLinkSitTarget() &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Also see===&lt;br /&gt;
&lt;br /&gt;
* [[User:Becky_Pippen/Script_Memory_Limits| Scripter&#039;s checklist of things you can do to conserve memory]]&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Limits_FAQ| Script memory limits FAQ]]&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/New_LSL_Functions&amp;diff=800773</id>
		<title>User:Becky Pippen/New LSL Functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/New_LSL_Functions&amp;diff=800773"/>
		<updated>2010-03-12T22:01:02Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: a few comments on the challenges of resizing.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== New LSL Functions ==&lt;br /&gt;
&lt;br /&gt;
The information in this section is preliminary and predictive, based on information given by&lt;br /&gt;
[[Babbage_Linden|Babbage Linden at his Office Hours]] and in [https://blogs.secondlife.com/community/technology/blog/2010/03/05/server-138-beta-now-open this SL Blog announcement].&lt;br /&gt;
&lt;br /&gt;
To help reduce the number of scripts needed for certain common operations, we will have these new&lt;br /&gt;
LSL functions in server version 1.38:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; list llGetLinkPrimitiveParams(integer linknumber, list params)&lt;br /&gt;
 &lt;br /&gt;
      llSetLinkPrimitiveParamsFast(integer link, list params)&lt;br /&gt;
 &lt;br /&gt;
      llLinkParticleSystem(integer link, list rules)&lt;br /&gt;
 &lt;br /&gt;
      llSetLinkTextureAnim(integer mode, integer face, integer sizex, integer sizey, float start, float length, float rate)&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will also get [[PRIM_TEXT]] for getting and setting hover text with [[llSetLinkPrimitiveParams]]().&lt;br /&gt;
The parameter list will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; PRIM_TEXT, string msg, vector color, float alpha &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===The deal with resizer scripts===&lt;br /&gt;
&lt;br /&gt;
Some content creators prefer to distribute their objects no-mod, yet still allow customers to resize or&lt;br /&gt;
retexture the linkset. Scripts can do that in a no-mod linkset, but the obvious solution using&lt;br /&gt;
[[llSetLinkPrimitiveParams]]() doesn&#039;t work well because of the 0.2-second delay built into the function.&lt;br /&gt;
To work around the delay, we have had to put a script in every child prim that can respond immediately&lt;br /&gt;
to a message from the root prim. When these functions become available, we will be able to do this kind of thing with a single script.&lt;br /&gt;
&lt;br /&gt;
===Fixing existing scripts===&lt;br /&gt;
&lt;br /&gt;
To fix such a script, locate the call to [[llMessageLinked]]() in the master script where it sends the link&lt;br /&gt;
message to the individual prims. It might look something vaguely like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llMessageLinked(LINK_whatever, command_resize, (string)newSize, NULL_KEY);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Replace that with a loop that applies whatever changes you need to each prim using&lt;br /&gt;
[[llSetLinkPrimitiveParamsFast]](). The prims have link numbers from 1 through [[llGetNumberOfPrims]](),&lt;br /&gt;
so you can make an efficient loop as shown here; just add any additional processing you&lt;br /&gt;
need for each prim inside the loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; applyChildParams(list ruleSet)&lt;br /&gt;
 {&lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum, ruleSet);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, to use this function to set the color and alpha of all the prims, build the parameter&lt;br /&gt;
list and pass it to applyChildParams() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;applyChildParams([PRIM_COLOR, ALL_SIDES, &amp;lt;0.9, 0.8, 0.2&amp;gt;, 1.0]);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
However, when you only need to apply a constant rule set like this to every prim in the linkset,&lt;br /&gt;
then the loop above can be simplified to a single statement;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llSetLinkPrimitiveParamsFast(LINK_SET, ruleSet);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or use [[LINK_ALL_OTHERS]] or [[LINK_ALL_CHILDREN]] instead of [[LINK_SET]] as appropriate.&lt;br /&gt;
&lt;br /&gt;
For another example, you can make a resizer script apply relative instead of absolute size changes by&lt;br /&gt;
using the new function [[llGetLinkPrimitiveParams]](). The script can read each child&#039;s current size,&lt;br /&gt;
then apply a resize factor and use [[llSetLinkPrimitiveParamsFast]]() to set a new size. This can help&lt;br /&gt;
make it easy for the script to do the right thing even if reset and all its variables get reinitialized. The&lt;br /&gt;
following function will resize all the prims (including the root itself) by a factor:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; rescaleLinksetByFactor(float factor)&lt;br /&gt;
 {&lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list params = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE]);&lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum,&lt;br /&gt;
             [PRIM_SIZE, factor * llList2Vector(params, 0)]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But what if the function above encounters a prim that, after resized, would have a dimension outside&lt;br /&gt;
the legal range of 0.01m to 10.0m? That could have unfortunate consequences, so&lt;br /&gt;
here is a function that resizes all the prims by a specified factor but only if all the resulting&lt;br /&gt;
dimensions in all the prims will all be in the legal range of 0.01m to 10.0m. If any dimension in&lt;br /&gt;
any prim would go out of those bounds, the function will return FALSE without resizing anything.&lt;br /&gt;
This example also includes repositioning the child prims within the linkset. Change or add whatever&lt;br /&gt;
additional parameters are needed for your particular application.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // This is not a complete resizer solution; it&#039;s just a demo of&lt;br /&gt;
 // how to call the new LSL functions.&lt;br /&gt;
 &lt;br /&gt;
 // Set the minimum and maximum size allowed for any prim in the linkset:&lt;br /&gt;
 //&lt;br /&gt;
 float minPrimSize = 0.01;&lt;br /&gt;
 float maxPrimSize = 10.0;&lt;br /&gt;
 &lt;br /&gt;
 // Resize all the prims in the linkset. For example, to resize all&lt;br /&gt;
 // the prims to 1% smaller, call this function with factor=0.99&lt;br /&gt;
 // Returns TRUE if successful, or FALSE if resizing would have&lt;br /&gt;
 // set a prim size out of bounds.&lt;br /&gt;
 //&lt;br /&gt;
 integer resizeLinksetWithBounds(float factor)&lt;br /&gt;
 {&lt;br /&gt;
     // Test to see if it&#039;s safe to change all the prim sizes:&lt;br /&gt;
 &lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list params = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE]);&lt;br /&gt;
         vector sz = factor * llList2Vector(params, 0);&lt;br /&gt;
         if (sz.x &amp;lt; minPrimSize || sz.x &amp;gt; maxPrimSize ||&lt;br /&gt;
             sz.y &amp;lt; minPrimSize || sz.y &amp;gt; maxPrimSize ||&lt;br /&gt;
             sz.z &amp;lt; minPrimSize || sz.z &amp;gt; maxPrimSize) {&lt;br /&gt;
                 return FALSE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     // We&#039;ve determined that we can safely change all the sizes, so&lt;br /&gt;
     // let&#039;s do so now.&lt;br /&gt;
 &lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list oldParams = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE, PRIM_POSITION]);&lt;br /&gt;
 &lt;br /&gt;
         list newParams = [ PRIM_SIZE, factor * llList2Vector(oldParams, 0) ];&lt;br /&gt;
         if (linkNum &amp;gt; 1) {&lt;br /&gt;
             vector linkPos = (vector)llList2String(oldParams, 1);&lt;br /&gt;
             vector localPos = (linkPos - llGetRootPosition()) / llGetRootRotation();&lt;br /&gt;
             newParams += [ PRIM_POSITION, factor * localPos ];&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum, newParams);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return TRUE;&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&#039;s a silly example of how it&#039;s used:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Example of how to call resizeLinksetWithBounds()&lt;br /&gt;
 //&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         float factor = llPow(llFrand(2.0), 2.0); // random factor for demo&lt;br /&gt;
         if (resizeLinksetWithBounds(factor)) {&lt;br /&gt;
             llOwnerSay(&amp;quot;Resized successfully by factor = &amp;quot; + (string)factor);&lt;br /&gt;
         } else {&lt;br /&gt;
             llOwnerSay(&amp;quot;Oops, can&#039;t resize by factor &amp;quot; + (string)factor);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
(Acknowledgements: Thanks to Keith Reinard for testing and improving the example above.)&lt;br /&gt;
&lt;br /&gt;
===Resizer challenges===&lt;br /&gt;
&lt;br /&gt;
A complete resizer solution is outside the scope of this article, but might need to consider such things as:&lt;br /&gt;
* You might need a way to reset all the prims to a known size, position, and orientation in case a resize loop breaks before successfully resizing all the prims.&lt;br /&gt;
* The example above might give unexpected results if executed while the object is in motion. For example, there is no way to guarantee that the functions llGetLinkPrimitiveParams() and llGetRootPosition() will be evaluated during the same simulator frame, so the local offset of a child prim could be computed incorrectly as the difference of the root position in one frame and the child position in another frame.&lt;br /&gt;
* Roundoff errors could accumulate if proportional scaling is done &#039;&#039;many&#039;&#039; times. (See [[User_talk:Becky_Pippen/New_LSL_Functions|the Discussion page]].)&lt;br /&gt;
* It&#039;s easy to scale a linkset so that prims get moved beyond their [[Linkability_Rules|linkability limits]], and no easy way for a script to test for this in advance. For more information, see the discussions at {{JIRA|SVC-5328}} and {{JIRA|SVC-5166}}.&lt;br /&gt;
&lt;br /&gt;
===Long range plans===&lt;br /&gt;
&lt;br /&gt;
The new LSL features described above are expected to appear in server version 1.38. Babbage Linden also mentioned a few additional LSL features he would like to implement sometime after 1.38, but with no guarantees if or when:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; PRIM_NAME, PRIM_DESC  // for llGet/SetPrimitiveParams()&lt;br /&gt;
 llAvatarOnLinkSitTarget()&lt;br /&gt;
 llLinkSitTarget() &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Also see===&lt;br /&gt;
&lt;br /&gt;
* [[User:Becky_Pippen/Script_Memory_Limits| Scripter&#039;s checklist of things you can do to conserve memory]]&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Limits_FAQ| Script memory limits FAQ]]&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/New_LSL_Functions&amp;diff=798952</id>
		<title>User:Becky Pippen/New LSL Functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/New_LSL_Functions&amp;diff=798952"/>
		<updated>2010-03-11T20:43:57Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Fix local vs. global with PRIM_POSITION&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== New LSL Functions ==&lt;br /&gt;
&lt;br /&gt;
The information in this section is preliminary and predictive, based on information given by&lt;br /&gt;
[[Babbage_Linden|Babbage Linden at his Office Hours]].&lt;br /&gt;
&lt;br /&gt;
To help reduce the number of scripts needed for certain common operations, we will have these new&lt;br /&gt;
LSL functions, perhaps as early as server version 1.38:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; list llGetLinkPrimitiveParams(integer linknumber, list params)&lt;br /&gt;
 &lt;br /&gt;
      llSetLinkPrimitiveParamsFast(integer link, list params)&lt;br /&gt;
 &lt;br /&gt;
      llLinkParticleSystem(integer link, list rules)&lt;br /&gt;
 &lt;br /&gt;
      llSetLinkTextureAnim(integer mode, integer face, integer sizex, integer sizey, float start, float length, float rate)&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will also get PRIM_TEXT for getting and setting hover text with [[llSetLinkPrimitiveParams]]().&lt;br /&gt;
The parameter list will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; PRIM_TEXT, string msg, vector color, float alpha &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===The deal with resizer scripts===&lt;br /&gt;
&lt;br /&gt;
Some content creators prefer to distribute their objects no-mod, yet still allow customers to resize or&lt;br /&gt;
retexture the linkset. Scripts can do that in a no-mod linkset, but the obvious solution using&lt;br /&gt;
[[llSetLinkPrimitiveParams]]() doesn&#039;t work well because of the 0.2-second delay built into the function.&lt;br /&gt;
To work around the delay, we have had to put a script in every child prim that can respond immediately&lt;br /&gt;
to a message from the root prim. When these functions become available, we will be able to do this kind of thing with a single script.&lt;br /&gt;
&lt;br /&gt;
===Fixing existing scripts===&lt;br /&gt;
&lt;br /&gt;
To fix such a script, locate the call to [[llMessageLinked]]() in the master script where it sends the link&lt;br /&gt;
message to the individual prims. It might look something vaguely like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llMessageLinked(LINK_whatever, command_resize, (string)newSize, NULL_KEY);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Replace that with a loop that applies whatever changes you need to each prim using&lt;br /&gt;
[[llSetLinkPrimitiveParamsFast]](). The prims have link numbers from 1 through [[llGetNumberOfPrims]](),&lt;br /&gt;
so you can make an efficient loop as shown here; just add any additional processing you&lt;br /&gt;
need for each prim inside the loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; applyChildParams(list ruleSet)&lt;br /&gt;
 {&lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum, ruleSet);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, to use this function to set the color and alpha of all the prims, build the parameter&lt;br /&gt;
list and pass it to applyChildParams() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;applyChildParams([PRIM_COLOR, ALL_SIDES, &amp;lt;0.9, 0.8, 0.2&amp;gt;, 1.0]);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
However, when you only need to apply a constant rule set like this to every prim in the linkset,&lt;br /&gt;
then the loop above can be simplified to a single statement;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llSetLinkPrimitiveParamsFast(LINK_SET, ruleSet);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or use [[LINK_ALL_OTHERS]] or [[LINK_ALL_CHILDREN]] instead of [[LINK_SET]] as appropriate.&lt;br /&gt;
&lt;br /&gt;
For another example, you can make a resizer script apply relative instead of absolute size changes by&lt;br /&gt;
using the new function [[llGetLinkPrimitiveParams]](). The script can read each child&#039;s current size,&lt;br /&gt;
then apply a resize factor and use [[llSetLinkPrimitiveParamsFast]]() to set a new size. This can help&lt;br /&gt;
make it easy for the script to do the right thing even if reset and all its variables get reinitialized. The&lt;br /&gt;
following function will resize all the prims (including the root itself) by a factor:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; rescaleLinksetByFactor(float factor)&lt;br /&gt;
 {&lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list params = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE]);&lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum,&lt;br /&gt;
             [PRIM_SIZE, factor * llList2Vector(params, 0)]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But what if the function above encounters a prim that, after resized, would have a dimension outside&lt;br /&gt;
the legal range of 0.01m to 10.0m? That could have unfortunate consequences, so&lt;br /&gt;
here is a function that resizes all the prims by a specified factor but only if all the resulting&lt;br /&gt;
dimensions in all the prims will all be in the legal range of 0.01m to 10.0m. If any dimension in&lt;br /&gt;
any prim would go out of those bounds, the function will return FALSE without resizing anything.&lt;br /&gt;
This example also includes repositioning the child prims within the linkset. Change or add whatever&lt;br /&gt;
additional parameters are needed for your particular application.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // This is not a complete resizer solution; it&#039;s just a demo of&lt;br /&gt;
 // how to call the new LSL functions.&lt;br /&gt;
 &lt;br /&gt;
 // Set the minimum and maximum size allowed for any prim in the linkset:&lt;br /&gt;
 //&lt;br /&gt;
 float minPrimSize = 0.01;&lt;br /&gt;
 float maxPrimSize = 10.0;&lt;br /&gt;
 &lt;br /&gt;
 // Resize all the prims in the linkset. For example, to resize all&lt;br /&gt;
 // the prims to 1% smaller, call this function with factor=0.99&lt;br /&gt;
 // Returns TRUE if successful, or FALSE if resizing would have&lt;br /&gt;
 // set a prim size out of bounds.&lt;br /&gt;
 //&lt;br /&gt;
 integer resizeLinksetWithBounds(float factor)&lt;br /&gt;
 {&lt;br /&gt;
     // Test to see if it&#039;s safe to change all the prim sizes:&lt;br /&gt;
 &lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list params = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE]);&lt;br /&gt;
         vector sz = factor * llList2Vector(params, 0);&lt;br /&gt;
         if (sz.x &amp;lt; minPrimSize || sz.x &amp;gt; maxPrimSize ||&lt;br /&gt;
             sz.y &amp;lt; minPrimSize || sz.y &amp;gt; maxPrimSize ||&lt;br /&gt;
             sz.z &amp;lt; minPrimSize || sz.z &amp;gt; maxPrimSize) {&lt;br /&gt;
                 return FALSE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     // We&#039;ve determined that we can safely change all the sizes, so&lt;br /&gt;
     // let&#039;s do so now.&lt;br /&gt;
 &lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list oldParams = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE, PRIM_POSITION]);&lt;br /&gt;
 &lt;br /&gt;
         list newParams = [ PRIM_SIZE, factor * llList2Vector(oldParams, 0) ];&lt;br /&gt;
         if (linkNum &amp;gt; 1) {&lt;br /&gt;
             vector linkPos = (vector)llList2String(oldParams, 1);&lt;br /&gt;
             vector localPos = (linkPos - llGetRootPosition()) / llGetRootRotation();&lt;br /&gt;
             newParams += [ PRIM_POSITION, factor * localPos ];&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum, newParams);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return TRUE;&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&#039;s a silly example of how it&#039;s used:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Example of how to call resizeLinksetWithBounds()&lt;br /&gt;
 //&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         float factor = llPow(llFrand(2.0), 2.0); // random factor for demo&lt;br /&gt;
         if (resizeLinksetWithBounds(factor)) {&lt;br /&gt;
             llOwnerSay(&amp;quot;Resized successfully by factor = &amp;quot; + (string)factor);&lt;br /&gt;
         } else {&lt;br /&gt;
             llOwnerSay(&amp;quot;Oops, can&#039;t resize by factor &amp;quot; + (string)factor);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
(Acknowledgements: Thanks to Keith Reinard for testing and improving this example.)&lt;br /&gt;
&lt;br /&gt;
===Long range plans===&lt;br /&gt;
&lt;br /&gt;
The new LSL features described above are expected to appear in server version 1.38. Babbage Linden also mentioned a few additional LSL features he would like to implement sometime after 1.38, but with no guarantees if or when:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; PRIM_NAME, PRIM_DESC  // for llGet/SetPrimitiveParams()&lt;br /&gt;
 llAvatarOnLinkSitTarget()&lt;br /&gt;
 llLinkSitTarget() &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Also see===&lt;br /&gt;
&lt;br /&gt;
* [[User:Becky_Pippen/Script_Memory_Limits| Scripter&#039;s checklist of things you can do to conserve memory]]&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Limits_FAQ| Script memory limits FAQ]]&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Memory_Limits_FAQ&amp;diff=791692</id>
		<title>User:Becky Pippen/Memory Limits FAQ</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Memory_Limits_FAQ&amp;diff=791692"/>
		<updated>2010-03-07T20:00:32Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== FAQ - Script Memory Limits ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Why do we need script memory limits?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; In some regions, the simulator uses so much script memory that it has to swap virtual memory to disk. That causes major lag, and lag is among the top complaints about Second Life.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; How can I tell if I&#039;m a heavy script user?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Starting with server version 1.38, we&#039;ll have new ways built into the sim and viewer to see our individual script memory usage, similar to how estate managers can see top scripts in a region. Also scripters can use the function [[LlGetFreeMemory|llGetFreeMemory()]] to get an approximate idea of memory usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will this break content?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Yes, some. The limits are specifically designed to limit the worst 5% of memory consumers so that the remaining 95% can continue to run freely without causing the simulator to swap virtual memory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; When will all this happen?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Script memory reporting and the new memory-efficient LSL functions are [https://blogs.secondlife.com/community/technology/blog/2010/03/05/server-138-beta-now-open available for testing on the Preview Grid] using server version 1.38 and beta 2.0 of the SL viewer. Server version 1.40 or later will have &amp;quot;Small Scripts&amp;quot; capability (see below). Memory limits will not be enforced until later in 2010 after these features have been deployed on the main grid for some time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will the limits be per avatar or per region?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; There will be limits on avatar, parcel, and region in a way that parallels [[LSL_http_server#Resource_Limitations|how URL resources are managed]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will Linden Lab pay for all the scripted products we bought that will no longer work?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Most products are reasonably scripted and will continue to work just fine. The only problematic products will be unusual ones that are scripted in ways that use unreasonable amounts of RAM. Try to get the product creator to make a new version that uses less memory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will we see a reduction in lag across the grid?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; It won&#039;t have a major impact on most regions. However, it will make a huge improvement on the 5% of the regions that use so much script memory that the server has to swap virtual memory to disk. Theoretically it might also somewhat improve performance on any other simulators running on the same server host using the same disk resources.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; How much RAM are we talking about?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; In the 95% of the regions that run smoothly, the simulator uses less than 800MB of RAM for everything except scripts, and up to 300MB for all the scripts in the region, and that&#039;s ok.  In the worst 5% of the regions, simulators use up to 2GB of RAM on the server, and that&#039;s not ok.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Why not just add RAM to the servers? RAM is cheap.&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Several thousand Xeon server boards at, say, US$100 each to upgrade, comes out to a bunch of money, even for Linden Lab.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Who will be affected?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; All scripts, old and new, will be subject to the new memory limits. When planning for the memory limits, [http://wiki.secondlife.com/wiki/User:Babbage_Linden/Office_Hours/2009_11_25 Babbage Linden said], &amp;quot;[2009/11/25 3:43]  Babbage Linden: ideally we&#039;d like to have 95+% of people use scripts as they currently do while still having 95+% of simulators running without swapping&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What&#039;s the difference between &amp;quot;Small Scripts Project,&amp;quot; &amp;quot;Big Scripts Project,&amp;quot; and &amp;quot;Efficient Scripts Project?&amp;quot;&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; The &#039;&#039;Efficient Scripts Project&#039;&#039; is the overall effort to reduce the memory consumption of Mono-based scripts, including limits on memory usage. The &#039;&#039;Small Scripts Project&#039;&#039; and &#039;&#039;Big Scripts Project&#039;&#039; both refer to a refinement where each script can request the specific amount of memory it anticipates using instead of being charged with a constant 16KB for LSL or 64KB for Mono scripts. So, for example, a simple Mono sit script might request only 3KB while another data-intensive Mono script might request several megabytes. Since the average Mono script in SL needs only about 9KB, this could help reduce overall script memory usage in a typical region. In [http://wiki.secondlife.com/wiki/User:Babbage_Linden/Office_Hours/2010_03_03 Office Hours], Babbage Linden said, &amp;quot;I would like to ship script usage, then small scripts, then script limits, then big scripts... so it would be nice to have mono scripts be able to reserve a lower memory amount before script limits enforcement happens.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What&#039;s the &amp;quot;LSL Penalty?&amp;quot;&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; At one time, LL considered charging LSL scripts with more memory than they actually consume as an incentive for scripters to use Mono. That idea has been dropped, and the current plan is to charge all scripts with the actual memory they reserve — currently 16KB for LSL, and 64KB for Mono scripts. In the future after the Small Scripts and Big Scripts Project have been implemented, Mono scripts can be charged for only the amount of memory they reserve, which could be more or less than 64KB. Discussion of this can be found in the [http://lists.secondlife.com/pipermail/opensource-dev/ opensource-dev] mailing list.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What can I do as a content creator to prepare?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Be kind to your customers and give them product updates using the [[User:Becky_Pippen/New_LSL_Functions| new efficient LSL functions]] when they become available. Learn more about how to [[User:Becky_Pippen/Measure_Script_Memory_Usage|measure memory usage]] and the [[User:Becky_Pippen/Script_Memory_Limits|scripting techniques for reducing memory]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What can I do as a customer?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; 95% of scripts are just fine and you don&#039;t need to do anything. If you are wearing hair, shoes, or other attachments with a script in every prim, then you can contact the creator and ask if there is a product update that uses the new LSL memory-efficient functions.&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Memory_Limits_FAQ&amp;diff=741552</id>
		<title>User:Becky Pippen/Memory Limits FAQ</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Memory_Limits_FAQ&amp;diff=741552"/>
		<updated>2010-02-25T01:44:28Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Minor clarifications from Babbage&amp;#039;s office hours this week&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== FAQ - Script Memory Limits ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Why do we need script memory limits?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; In some regions, the simulator uses so much script memory that it has to swap virtual memory to disk. That causes major lag, and lag is among the top complaints about Second Life.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; How can I tell if I&#039;m a heavy script user?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Starting with server version 1.38, we&#039;ll have new ways built into the sim and viewer to see our individual script memory usage, similar to how estate managers can see top scripts in a region. Also scripters can use the function [[LlGetFreeMemory|llGetFreeMemory()]] to get an approximate idea of memory usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will this break content?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Yes, some. The limits are specifically designed to limit the worst 5% of memory consumers so that the remaining 95% can continue to run freely without causing the simulator to swap virtual memory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; When will all this happen?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Before the end of Q1 of 2010, the new LSL functions and the new UI for displaying memory usage should be available for testing on the Preview Grid, then deployed to the main grid in server version 1.38 in Q2. We&#039;ll have several months where we can experiment with the new LSL functions and tools on the preview grid, then on the main grid before the memory limits are enforced sometime later in 2010.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will the limits be per avatar or per region?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; That hasn&#039;t been determined yet, but it will probably involve limits on avatar, parcel, and region in a way that parallels [[LSL_http_server#Resource_Limitations|how URL resources are managed]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will Linden Lab pay for all the scripted products we bought that will no longer work?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Most products are reasonably scripted and will continue to work just fine. The only problematic products will be unusual ones that are scripted in ways that use unreasonable amounts of RAM. Try to get the product creator to make a new version that uses less memory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will we see a reduction in lag across the grid?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; It won&#039;t have a major impact on most regions. However, it will make a huge improvement on the 5% of the regions that use so much script memory that the server has to swap virtual memory to disk. Theoretically it might also somewhat improve performance on any other simulators running on the same server host using the same disk resources.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; How much RAM are we talking about?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; In the 95% of the regions that run smoothly, the simulator uses less than 800MB of RAM for everything except scripts, and up to 300MB for all the scripts in the region, and that&#039;s ok.  In the worst 5% of the regions, simulators use up to 2GB of RAM on the server, and that&#039;s not ok.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Why not just add RAM to the servers? RAM is cheap.&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Several thousand Xeon server boards at, say, US$100 each to upgrade, comes out to a bunch of money, even for Linden Lab.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Who will be affected?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; All scripts, old and new, will be subject to the new memory limits. When planning for the memory limits, Babbage Linden said, &amp;quot;[2009/11/25 3:43]  Babbage Linden: ideally we&#039;d like to have 95+% of people use scripts as they currently do while still having 95+% of simulators running without swapping&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What&#039;s the difference between &amp;quot;Small Scripts Project,&amp;quot; &amp;quot;Big Scripts Project,&amp;quot; and &amp;quot;Efficient Scripts Project?&amp;quot;&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; The &#039;&#039;Efficient Scripts Project&#039;&#039; is the overall effort to reduce the memory consumption of Mono-based scripts, including limits on memory usage. The &#039;&#039;Small Scripts Project&#039;&#039; and &#039;&#039;Big Scripts Project&#039;&#039; both refer to a refinement where each script can request the specific amount of memory it anticipates using instead of being charged with a constant 16KB for LSL or 64KB for Mono scripts. So, for example, a simple Mono sit script might request only 3KB while another data-intensive Mono script might request several megabytes. Since the average Mono script in SL needs only about 9KB, this could help reduce overall script memory usage in a typical region. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What&#039;s the &amp;quot;LSL Penalty?&amp;quot;&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; At one time, LL considered charging LSL scripts with more memory than they actually consume as an incentive for scripters to use Mono. That idea has been dropped, and the current plan is to charge LSL scripts with actual memory use — currently 16KB for LSL, and 64KB for Mono scripts. In the future after the Small Scripts and Big Scripts Project has been implemented, Mono scripts can be charged for only the amount of memory they reserve, which could be more or less than 64KB.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What can I do as a content creator to prepare?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Be kind to your customers and give them product updates using the [[User:Becky_Pippen/New_LSL_Functions| new efficient LSL functions]] when they become available. Learn more about how to [[User:Becky_Pippen/Measure_Script_Memory_Usage|measure memory usage]] and the [[User:Becky_Pippen/Script_Memory_Limits|scripting techniques for reducing memory]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What can I do as a customer?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; 95% of scripts are just fine and you don&#039;t need to do anything. If you are wearing hair, shoes, or other attachments with a script in every prim, then you can contact the creator and ask if there is a product update that uses the new LSL memory-efficient functions.&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen&amp;diff=729532</id>
		<title>User:Becky Pippen</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen&amp;diff=729532"/>
		<updated>2010-02-17T18:42:22Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Moved the LSL performance data to User:Becky_Pippen/LSL_Performance&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== About ==&lt;br /&gt;
I love helping new residents get addicted... er, I mean acclimated to Second Life. Here are some notes I&#039;ve picked up along the way that might be of help.&lt;br /&gt;
== SL Glossary ==&lt;br /&gt;
[http://www.inkwelle.com/glossary_nci.shtml Second Life&#039;s most complete glossary]&lt;br /&gt;
== What is Mono? ==&lt;br /&gt;
=== John and Mary ===&lt;br /&gt;
John and Mary are LSL scripters. John prefers the old-fashioned way of doing things, while Mary enjoys all the latest new technology, like Mono. They both write LSL scripts. John runs his scripts under the old way, and Mary runs hers using Mono.&lt;br /&gt;
&lt;br /&gt;
When John writes an LSL script and clicks the Save button, the client viewer compiles the script into proprietary bytecode and uploads it to the servers. The server runs the script by using a proprietary interpreter to interpret the bytecode. It runs slow.&lt;br /&gt;
&lt;br /&gt;
Mary writes the identical script -- no change in the LSL language syntax. When she presses &amp;quot;Save&amp;quot;, the client uploads her script text to the servers where it gets compiled into standardized CIL assembly language. (CIL is a bytecode that originally came from Microsoft&#039;s .NET technology.) Because the Linden servers are Linux machines, there&#039;s no .NET framework  available to run the CIL code. So the servers use the open-source Mono framework to execute the CIL. Mono includes an open-source JIT (&amp;quot;Just in Time&amp;quot;) compiler that converts the CIL bytecode into machine code as it&#039;s needed, and that compiled machine code is what makes the script execute faster than the old way.&lt;br /&gt;
=== How it all Flows ===&lt;br /&gt;
Old way:&lt;br /&gt;
 LSL  ---&amp;gt; LSL compiler ---&amp;gt; proprietary bytecode ---&amp;gt; LSL bytecode interpreter&lt;br /&gt;
&lt;br /&gt;
New way:&lt;br /&gt;
 LSL                        ---&amp;gt; LSL compiler   | &lt;br /&gt;
 C# (future)                ---&amp;gt; C# compiler    |  ---&amp;gt; CIL bytecode ---&amp;gt; Mono CIL execution engine&lt;br /&gt;
 Other languages (future)   ---&amp;gt; other compiler |&lt;br /&gt;
=== Do Scripts Running in Mono Get More Memory? ===&lt;br /&gt;
Yes, it&#039;s true that each Mono script gets 64K of memory to use. Non-Mono scripts have to fit inside 16K of memory. (That&#039;s compiled code plus stack plus heap.) Unfortunately, Mono-compiled code is a bit of a memory hog and can take up to 4 times as much memory as the old runtime environment. So, scripts using Mono end up with a little more memory to play with depending on the code-to-data ratio, but not four times as much.&lt;br /&gt;
=== Evolving Terminology ===&lt;br /&gt;
LSL - Used to refer to the whole system -- the language, the front-end compiler, and the back-end interpreter. Now it&#039;s confusing because it&#039;s used in one of two ways and its meaning depends on the context:  (1) LSL is the language, the source code, which has not changed with Mono. E.g., &amp;quot;Take this LSL script and compile it with Mono and it will run faster.&amp;quot; Or (2) LSL refers to the old compiler front-end and interpreter virtual machine back-end. E.g., &amp;quot;When I compile this script with LSL it runs very slowly.&amp;quot; Both meanings are used in the sentence, &amp;quot;This LSL script was compiled with LSL, but that LSL script was compiled with Mono.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The term LSO refers to the bytecode generated by the traditional LSL compiler and executed by the old LSL runtime, and is a term that can be used to distinguish the old LSL tool set from the Mono tool set.&lt;br /&gt;
=== Can We Use Other Languages? ===&lt;br /&gt;
Mono just runs CIL bytecode. It doesn&#039;t know or care what the original language was. At this time (mid-2008) only LSL source code can be compiled into CIL and run on Mono. But maybe someday we will be able to compile C# and possibly other languages into CIL which will run under Mono. A single object could contain scripts that originally were written in different languages. &lt;br /&gt;
&lt;br /&gt;
The LSL language -- the source syntax -- will probably be supported for a very long time. &lt;br /&gt;
&lt;br /&gt;
However, the servers will support both back-ends for a very long time -- the one that executes the old proprietary LSL bytecode, and the new Mono virtual machine that executes standard CIL. All the millions of existing LSL scripts compiled the old way will continue to run indefinitely. New LSL scripts may be compiled using the old LSL compiler or the new Mono compiler.&lt;br /&gt;
=== What To Expect After Mono Hits the Main Grid ===&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;Old LSL scripts already compiled:&#039;&#039;&#039;&#039;&#039; will continue to run indefinitely. You don&#039;t have to do a thing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;Any LSL script if you have the source code:&#039;&#039;&#039;&#039;&#039; can be recompiled under the old LSL compiler or the new Mono compiler, at your choice, for a long time to come.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;C# and other languages:&#039;&#039;&#039;&#039;&#039; probably added at some future time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== LSL Timings and Rates ==&lt;br /&gt;
Updated and [[User:Becky_Pippen/LSL_Performance|moved to this page]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Animation Frame Rate ==&lt;br /&gt;
=== The Problem ===&lt;br /&gt;
You may have heard rumors that animations (BVH files) get reduced to 12 FPS when uploaded or played, and there&#039;s no need to make an animation faster than 12 frames per second (FPS). Other rumors say that fast animation frame rates give smoother animations, while other rumors say that you should use the lowest animation frame rate possible. Confused? Here&#039;s some information that might help.&lt;br /&gt;
=== The SL Viewer Interpolates ===&lt;br /&gt;
Part of the confusion is that the term &amp;quot;frame rate&amp;quot; means different things in different contexts. The SL viewer renders frames of the scene, including any animations in the scene, as fast as it can. You can always see how fast the client is rendering frames by looking for the FPS number in the Basic section in the Statistics Bar (Ctrl-Shift-1). The frame rate in an animation file is just there to establish the time scale of the animation information. It&#039;s a different kind of &amp;quot;frame.&amp;quot; So how do the two &amp;quot;frame rates&amp;quot; relate and how does it all work? It&#039;s simple -- when the viewer renders a frame, it determines how far along it is in the animation and interpolates the avatar&#039;s bone rotations based on the nearest two frames in the animation file.&lt;br /&gt;
&lt;br /&gt;
Refer to the following illustration to see how you can demonstrate this interpolation. Using your favorite animation editor, make an animation file consisting of just three or four frames. The first frame is the requisite reference frame, then frame 2 is the first key frame of the animation that defines the start of a movement. Frame 3 is the last frame of the movement. Optionally you can add a frame 4 that is a copy of frame2 to make a loop. Make a large movement between frames 2 and 3, such as arms straight down in frame 2 and straight up in frame 3. Set the animation for 1 frame per second. Upload the short animation file and set the looping parameters if needed so that the animation loops continuously.&lt;br /&gt;
&lt;br /&gt;
With the animation set to one frame per second, the arms should start at the sides, then one second later be raised above the head, then two seconds after the start of the animation, the arms should be back at the sides, then repeat. What happens if the client renders frames faster than one per second? If the viewer didn&#039;t interpolate, you would expect the avatar&#039;s arms to snap suddenly from one position to the other... snap up... snap down... etc. at one-second intervals. But that&#039;s not what happens at all. Regardless of your viewer frame rate, you&#039;ll see your avatar&#039;s hands moving smoothly, up and down, with all the intermediate positions between animation frames interpolated smoothly. To examine the effect in more detail, click Advanced-&amp;gt;Character-&amp;gt;Slow Motion Animations, then you&#039;ll have a whole bunch of interpolated rendered frames to examine.&lt;br /&gt;
&lt;br /&gt;
[[Image:Animation-interpolation.jpg|500px|||Viewer interpolates animation frames]]&lt;br /&gt;
&lt;br /&gt;
=== Asynchronous Frame Rates ===&lt;br /&gt;
In the following timeline example, the top lines represent the frames in an animation file, and the bottom lines show how fast the viewer renders frames and where the rendered frames correspond in time to the animation file. In this illustration, the animation frames are numbered starting at two, because frame one is the reference frame that never gets rendered. When the client is rendering the first frame of the animation (render frame 0 in the illustration), it corresponds to frame 2 in the animation file. In this illustration, the client is running at a fairly constant rate a little more than three times the rate of frames in the animation file:&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
animation frame number        2                         3                         4                         5&lt;br /&gt;
&lt;br /&gt;
animation frames:             K-------------------------K-------------------------K-------------------------K-----&lt;br /&gt;
&lt;br /&gt;
viewer rendered frames:       |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|&lt;br /&gt;
&lt;br /&gt;
viewer frame number:          0       1       2       3       4       5       6       7       8       9      10&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
When the viewer is rendering frame 0, the bone rotations exactly correspond to frame 2 in the animation file. After that, the time when a frame is rendered will almost always fall somewhere between two of the animation frames and the client will interpolate the bone rotations. For example, when the client renders frame 1, the bone rotations will be something between the rotations in frames 2 and 3 of the animation file, but closer to the rotations in frame 2. &lt;br /&gt;
&lt;br /&gt;
If the client is running slow in comparison to the animation file, then some data in the animation file must be skipped:&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
animation frame number        2     3     4     5     6     7     8     9    10    11    12    13    14    15 &lt;br /&gt;
&lt;br /&gt;
animation key frames:         K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K---&lt;br /&gt;
&lt;br /&gt;
viewer rendered frames:       |--------------|---------------|---------------|---------------|---------------|--&lt;br /&gt;
&lt;br /&gt;
viewer frame number:          0              1               2               3               4               5 &lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
In this scenario, when it&#039;s time for the client to render frame 1, the animation information is interpolated from frames 4 and 5 in the animation file, which means that any quick movement defined in frames 3 and 4 of the animation file will have been ignored that time around. If the animation loops, then perhaps on a subsequent loop the frames will align differently and a different set of animation frames will be ignored.&lt;br /&gt;
&lt;br /&gt;
=== Other Considerations ===&lt;br /&gt;
There are additional forces at work you need to be aware of:&lt;br /&gt;
&lt;br /&gt;
1. The higher the animation frame rate, the larger the file that has to be sent from the sim to every client in viewing range of the animation. Smaller is less laggy.&lt;br /&gt;
&lt;br /&gt;
2. When you upload an animation, the viewer silently drops small movements. It does this by comparing each bone&#039;s rotation in two adjacent frames, then dropping any data for bones that don&#039;t move much. The exact algorithm is complicated, but in general the threshold is a few degrees in any axis from one frame to the next. If a bone&#039;s rotation changes from one frame to the next less than 3 or 4 degrees in all axes, it might be dropped. If the movement is more than about 10 degrees in any axis, it&#039;s likely to be retained. If an animation is made with a high frame rate, then the movement from frame to frame will be smaller and more movements will be dropped. If the animation frames are less frequent, then the movement between frames will be larger and more likely to be retained.&lt;br /&gt;
&lt;br /&gt;
3. When the viewer interpolates bone rotations from the animation file, it uses a linear interpolation between the nearest frames, which sometimes looks a bit robotic. A higher animation rate lets you specify movements in finer resolution and make the movement start slow, get faster in the middle of the movement, then slow down when it reaches its maximum extent. &lt;br /&gt;
&lt;br /&gt;
=== Summary ===&lt;br /&gt;
The last two forces mentioned above work against each other, so the challenge for animators is to find a balance. The animation needs to be made with a high enough frame rate to describe the movements the way you want them to appear, yet it needs to be as low a frame rate as possible to avoid having the client discard small movements on upload. It depends on the animation, but a good starting point might be around 10 FPS for the animation. Try a lower animation frame rate if the animation appears ok with fewer frames, relying more on the linear interpolation by the client to smooth it out. Use a higher animation frame rate if the linear interpolation doesn&#039;t look quite right and you need finer resolution to describe more complex or non-linear movements.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Script Memory Limits ==&lt;br /&gt;
Script Memory limits are coming in 2010! Here&#039;s some information to help you prepare;&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Limits_FAQ| FAQ about script memory limits]]&lt;br /&gt;
* [[User:Becky_Pippen/Measure_Script_Memory_Usage|Measuring your script memory usage]]&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Efficiency|Calculating memory needed and memory usage efficiency]]&lt;br /&gt;
* [[User:Becky_Pippen/Script_Memory_Limits| A checklist for scripters -- techniques for reducing memory usage]]&lt;br /&gt;
* [[User:Becky_Pippen/New_LSL_Functions| New LSL functions llGetLinkPrimitiveParams(), llSetLinkPrimitiveParamsFast() and more]]&lt;br /&gt;
* [[User:Becky_Pippen/Hashing| Scripting techniques for hashing data to conserve memory]]&lt;br /&gt;
* [[User:Becky_Pippen/Numeric_Storage| Scripting techniques for storing arbitrary binary data without lists]]&lt;br /&gt;
* [[User:Becky_Pippen/Text_Storage| Scripting techniques for compressing ASCII text in Mono]]&lt;br /&gt;
* [http://lslwiki.net/lslwiki/wakka.php?wakka=MemoryUsage Empirical measurements of memory usage by data type and scope (lslwiki.net)]&lt;br /&gt;
* [[LSL_Script_Memory|Empirical measurements of memory usage by data type and scope (SL wiki)]]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [http://slurl.com/secondlife/Nevia/128/128/23 The Community of the Nevia Archipelago] [http://www.inkwelle.com Website]&lt;br /&gt;
* [http://jira.secondlife.com/ JIRA Issue Tracker]&lt;br /&gt;
* [https://wiki.secondlife.com/wiki/About_AWG Architecture Working Group]&lt;br /&gt;
* [https://wiki.secondlife.com/wiki/Mono Mono wiki]&lt;br /&gt;
{{skills&lt;br /&gt;
|Builder=*&lt;br /&gt;
|Scripter=*&lt;br /&gt;
|Terraformer=&lt;br /&gt;
|Architect=&lt;br /&gt;
|Scenographer=&lt;br /&gt;
|SLogistician=&lt;br /&gt;
|}}&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/New_LSL_Functions&amp;diff=729522</id>
		<title>User:Becky Pippen/New LSL Functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/New_LSL_Functions&amp;diff=729522"/>
		<updated>2010-02-17T18:25:40Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Minor updates from today&amp;#039;s office hours&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== New LSL Functions ==&lt;br /&gt;
&lt;br /&gt;
The information in this section is preliminary and predictive, based on information given by&lt;br /&gt;
[[Babbage_Linden|Babbage Linden at his Office Hours]].&lt;br /&gt;
&lt;br /&gt;
To help reduce the number of scripts needed for certain common operations, we will have these new&lt;br /&gt;
LSL functions, perhaps as early as server version 1.38:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; list llGetLinkPrimitiveParams(integer linknumber, list params)&lt;br /&gt;
 &lt;br /&gt;
      llSetLinkPrimitiveParamsFast(integer link, list params)&lt;br /&gt;
 &lt;br /&gt;
      llLinkParticleSystem(integer link, list rules)&lt;br /&gt;
 &lt;br /&gt;
      llSetLinkTextureAnim(integer mode, integer face, integer sizex, integer sizey, float start, float length, float rate)&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will also get PRIM_TEXT for getting and setting hover text with [[llSetLinkPrimitiveParams]]().&lt;br /&gt;
The parameter list will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; PRIM_TEXT, string msg, vector color, float alpha &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===The deal with resizer scripts===&lt;br /&gt;
&lt;br /&gt;
Some content creators prefer to distribute their objects no-mod, yet still allow customers to resize or&lt;br /&gt;
retexture the linkset. Scripts can do that in a no-mod linkset, but the obvious solution using&lt;br /&gt;
[[llSetLinkPrimitiveParams]]() doesn&#039;t work well because of the 0.2-second delay built into the function.&lt;br /&gt;
To work around the delay, we have had to put a script in every child prim that can respond immediately&lt;br /&gt;
to a message from the root prim. When these functions become available, we will be able to do this kind of thing with a single script.&lt;br /&gt;
&lt;br /&gt;
===Fixing existing scripts===&lt;br /&gt;
&lt;br /&gt;
To fix such a script, locate the call to [[llMessageLinked]]() in the master script where it sends the link&lt;br /&gt;
message to the individual prims. It might look something vaguely like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llMessageLinked(LINK_whatever, command_resize, (string)newSize, NULL_KEY);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Replace that with a loop that applies whatever changes you need to each prim using&lt;br /&gt;
[[llSetLinkPrimitiveParamsFast]](). The prims have link numbers from 1 through [[llGetNumberOfPrims]](),&lt;br /&gt;
so you can make an efficient loop as shown here; just add any additional processing you&lt;br /&gt;
need for each prim inside the loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; applyChildParams(list ruleSet)&lt;br /&gt;
 {&lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum, ruleSet);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, to use this function to set the color and alpha of all the prims, build the parameter&lt;br /&gt;
list and pass it to applyChildParams() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;applyChildParams([PRIM_COLOR, ALL_SIDES, &amp;lt;0.9, 0.8, 0.2&amp;gt;, 1.0]);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
However, when you only need to apply a constant rule set like this to every prim in the linkset,&lt;br /&gt;
then the loop above can be simplified to a single statement;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llSetLinkPrimitiveParamsFast(LINK_SET, ruleSet);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or use [[LINK_ALL_OTHERS]] or [[LINK_ALL_CHILDREN]] instead of [[LINK_SET]] as appropriate.&lt;br /&gt;
&lt;br /&gt;
For another example, you can make a resizer script apply relative instead of absolute size changes by&lt;br /&gt;
using the new function [[llGetLinkPrimitiveParams]](). The script can read each child&#039;s current size,&lt;br /&gt;
then apply a resize factor and use [[llSetLinkPrimitiveParamsFast]]() to set a new size. This can help&lt;br /&gt;
make it easy for the script to do the right thing even if reset and all its variables get reinitialized. The&lt;br /&gt;
following function will resize all the prims (including the root itself) by a factor:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; rescaleLinksetByFactor(float factor)&lt;br /&gt;
 {&lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list params = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE]);&lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum,&lt;br /&gt;
             [PRIM_SIZE, factor * llList2Vector(params, 0)]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But what if the function above encounters a prim that, after resized, would have a dimension outside&lt;br /&gt;
the legal range of 0.01m to 10.0m? That could have unfortunate consequences, so&lt;br /&gt;
here is a function that resizes all the prims by a specified factor but only if all the resulting&lt;br /&gt;
dimensions in all the prims will all be in the legal range of 0.01m to 10.0m. If any dimension in&lt;br /&gt;
any prim would go out of those bounds, the function will return FALSE without resizing anything.&lt;br /&gt;
This example also includes repositioning the child prims within the linkset. Change or add whatever&lt;br /&gt;
additional parameters are needed for your particular application.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // This is not a complete resizer solution; it&#039;s just a demo of&lt;br /&gt;
 // how to call the new LSL functions.&lt;br /&gt;
 &lt;br /&gt;
 // Set the minimum and maximum size allowed for any prim in the linkset:&lt;br /&gt;
 //&lt;br /&gt;
 float minPrimSize = 0.01;&lt;br /&gt;
 float maxPrimSize = 10.0;&lt;br /&gt;
 &lt;br /&gt;
 // Resize all the prims in the linkset. For example, to resize all&lt;br /&gt;
 // the prims to 1% smaller, call this function with factor=0.99&lt;br /&gt;
 // Returns TRUE if successful, or FALSE if resizing would have&lt;br /&gt;
 // set a prim size out of bounds.&lt;br /&gt;
 //&lt;br /&gt;
 integer resizeLinksetWithBounds(float factor)&lt;br /&gt;
 {&lt;br /&gt;
     // Test to see if it&#039;s safe to change all the prim sizes:&lt;br /&gt;
 &lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list params = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE]);&lt;br /&gt;
         vector sz = factor * llList2Vector(params, 0);&lt;br /&gt;
         if (sz.x &amp;lt; minPrimSize || sz.x &amp;gt; maxPrimSize ||&lt;br /&gt;
             sz.y &amp;lt; minPrimSize || sz.y &amp;gt; maxPrimSize ||&lt;br /&gt;
             sz.z &amp;lt; minPrimSize || sz.z &amp;gt; maxPrimSize) {&lt;br /&gt;
                 return FALSE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     // We&#039;ve determined that we can safely change all the sizes, so&lt;br /&gt;
     // let&#039;s do so now. This example assumes that llGetLinkPrimitiveParams()&lt;br /&gt;
     // with PRIM_POSITION will return the relative offset from the root prim,&lt;br /&gt;
     // but those details have not yet been published.&lt;br /&gt;
 &lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list oldParams = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE, PRIM_POSITION]);&lt;br /&gt;
 &lt;br /&gt;
         list newParams = [ PRIM_SIZE, factor * llList2Vector(oldParams, 0) ];&lt;br /&gt;
         if (linkNum &amp;gt; 1) {&lt;br /&gt;
             newParams += [ PRIM_POSITION, factor * llList2Vector(oldParams, 1) ];&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum, newParams);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return TRUE;&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&#039;s a silly example of how it&#039;s used:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Example of how to call resizeLinksetWithBounds()&lt;br /&gt;
 //&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         float factor = llPow(llFrand(2.0), 2.0); // random factor for demo&lt;br /&gt;
         if (resizeLinksetWithBounds(factor)) {&lt;br /&gt;
             llOwnerSay(&amp;quot;Resized successfully by factor = &amp;quot; + (string)factor);&lt;br /&gt;
         } else {&lt;br /&gt;
             llOwnerSay(&amp;quot;Oops, can&#039;t resize by factor &amp;quot; + (string)factor);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Long range plans===&lt;br /&gt;
&lt;br /&gt;
The new LSL features described above are expected to appear in server version 1.38. Babbage Linden also mentioned a few additional LSL features he would like to implement sometime after 1.38, but with no guarantees if or when:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; PRIM_NAME, PRIM_DESC  // for llGet/SetPrimitiveParams()&lt;br /&gt;
 llAvatarOnLinkSitTarget()&lt;br /&gt;
 llLinkSitTarget() &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Also see===&lt;br /&gt;
&lt;br /&gt;
* [[User:Becky_Pippen/Script_Memory_Limits| Scripter&#039;s checklist of things you can do to conserve memory]]&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Limits_FAQ| Script memory limits FAQ]]&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/LSL_Performance&amp;diff=729242</id>
		<title>User:Becky Pippen/LSL Performance</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/LSL_Performance&amp;diff=729242"/>
		<updated>2010-02-17T04:43:54Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: added Mono CIL; reformatted; in preparation for relocation from User:Becky_Pippen&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==LSL Timings and Rates==&lt;br /&gt;
Here are some selected LSL function times and event rates comparing scripts compiled and run using the old LSL toolset (a.k.a LSO), and using Mono.&lt;br /&gt;
&lt;br /&gt;
The basic test framework for these tests is a loop like shown below, where we take the difference in llGetTime() before and after the loop:&lt;br /&gt;
&lt;br /&gt;
 while (--i &amp;gt;= 0) {&lt;br /&gt;
     f(); f(); f(); f(); f(); f(); f(); f();&lt;br /&gt;
     f(); f(); f(); f(); f(); f(); f(); f();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The loop is partially unrolled to make the loop control structure overhead negligible. We take several measurements in multiple Class 5 sims that are running consistently with a total frame time under 10 ms, where each test runs for 30 - 120 minutes, or longer if we fall asleep during the test. We measure several times during a 24-hour period until the numbers converge with a variance of 10% or less.&lt;br /&gt;
&lt;br /&gt;
Measurements are taken in at least three regions, because we&#039;ve occasionally seen regions with lots of reported spare time per frame, yet scripts seem to run inexplicably slow. If two out of three regions converge and agree, the anomalous region is ignored. Consider the numbers reported on this page to be maximums possible in cooperative regions.&lt;br /&gt;
&lt;br /&gt;
In a Mono test, the result of the first run after script reset is ignored to eliminate the overhead of the VM initialization, JIT compilation, etc.&lt;br /&gt;
&lt;br /&gt;
The Mono CIL shown for some of the LSL code below was obtained by modifying an SL viewer so that it calls lscript_compile() when saving a script. (Scripts are compiled on the simulators nowadays, but Linden Lab has kindly left the script compiler code in the open source viewer source code.) Comments and formatting were added manually.&lt;br /&gt;
&lt;br /&gt;
Note that these are stopwatch times. How rapidly you can call a function or how often an event handler is invoked doesn&#039;t necessarily correspond to how much those functions and events lag a sim because of all the throttling and scheduling going on behind the scenes inside the run-time engine.&lt;br /&gt;
&lt;br /&gt;
Class 7 hosts are starting to appear on the main grid as reported [[Beta_Server_Office_Hours/Minutes/2010-02-04|here]] and [[User:Andrew_Linden/Office_Hours/2010_02_02|here]]. As Linden Lab moves toward classless regions, absolute timings like shown on this page will become less meaningful. Maybe we can make use of some sort of relative performance measurements in the future.&lt;br /&gt;
&lt;br /&gt;
== Functions, operators, &amp;amp; control structures ==&lt;br /&gt;
&lt;br /&gt;
===Floating point assignment===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|x = [[PI]];&lt;br /&gt;
where x is a global [[float]]&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.11&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.0007&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    .field public float32 &#039;x&#039;         // float x;&lt;br /&gt;
    . . .&lt;br /&gt;
    ldarg.0                           // push &#039;this&#039; pointer&lt;br /&gt;
    ldc.r8 (00 00 00 60 fb 21 09 40)  // push 64-bit PI&lt;br /&gt;
    stfld float32 LSL_script::&#039;x&#039;     // pop and store 32-bit x&lt;br /&gt;
    ldarg.0&lt;br /&gt;
    ldfld float32 LSL_script::&#039;x&#039;&lt;br /&gt;
    pop&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}  &lt;br /&gt;
:Notes: Verified and updated for server version 1.34.2.142087.&lt;br /&gt;
&lt;br /&gt;
===Call to empty f() { }===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|f();&lt;br /&gt;
where f() is defined as f() { }&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.33&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.0026&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
Function definition:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
 .method public hidebysig instance default void &#039;gf&#039;() cil managed&lt;br /&gt;
 {&lt;br /&gt;
     .maxstack 500&lt;br /&gt;
     ret&lt;br /&gt;
 }&amp;lt;/code&amp;gt;&lt;br /&gt;
Caller:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
     ldarg.0    // push &#039;this&#039; pointer&lt;br /&gt;
     call instance void class LSL_script::&#039;gf&#039;()&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[for]] loop overhead===&lt;br /&gt;
:This test measures the overhead of a [[for]] loop that iterates an empty block just once.&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[for]] (i = 0; i == 0; ++i) { }&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.07&#039;&#039;&#039; ms/loop&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.001&#039;&#039;&#039; ms/loop&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    // loop init:  i = 0&lt;br /&gt;
    ldc.i4.0      // push 32-bit 0&lt;br /&gt;
    stloc.s 0     // pop and store in i&lt;br /&gt;
    ldc.i4 0      // push 32-bit 0&lt;br /&gt;
    dup           // duplicate 0 on the stack&lt;br /&gt;
    stloc.s 0     // pop and store 0 in i&lt;br /&gt;
    pop           // cleanup&lt;br /&gt;
 &lt;br /&gt;
 LabelTempJump0:&lt;br /&gt;
    // top of loop: test i == 0&lt;br /&gt;
    ldc.i4 0      // push 32-bit 0&lt;br /&gt;
    ldloc.s 0     // push i&lt;br /&gt;
    ceq           // compare i and 0&lt;br /&gt;
    brfalse LabelTempJump1 // branch if i != 0&lt;br /&gt;
 &lt;br /&gt;
    // bottom of loop: ++i&lt;br /&gt;
    ldloc.s 0     // push i&lt;br /&gt;
    ldc.i4.1      // push 32-bit 1&lt;br /&gt;
    add           // pop i and 1, add, push result&lt;br /&gt;
    dup           // duplicate result&lt;br /&gt;
    stloc.s 0     // save result in i&lt;br /&gt;
    pop           // cleanup&lt;br /&gt;
    br LabelTempJump0&lt;br /&gt;
 &lt;br /&gt;
 LabelTempJump1:&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[State]] change===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[state]] a;&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.14&#039;&#039;&#039; ms/transition&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;22.2&#039;&#039;&#039; ms/transition&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0            // push &#039;this&#039; pointer&lt;br /&gt;
    ldstr &amp;quot;state2&amp;quot;&lt;br /&gt;
    call instance void class [LslUserScript] \&lt;br /&gt;
            LindenLab.SecondLife.LslUserScript::ChangeState(string)&lt;br /&gt;
    ret&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes:&lt;br /&gt;
:State changes in Mono are tied to the sim frame rate of 45 fps (per Vektor Linden). The test code for state changes is an outer loop around 16 state changes like this:&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
::    . . .&lt;br /&gt;
::    state state&#039;&#039;N&#039;&#039;   { state_entry() { state state&#039;&#039;N+1&#039;&#039;; } }&lt;br /&gt;
::    state state&#039;&#039;N+1&#039;&#039; { state_entry() { state state&#039;&#039;N+2&#039;&#039;; } }&lt;br /&gt;
::    . . . etc.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===[[Vector]] operations===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|v = &amp;lt;PI,[[PI]],PI&amp;gt; + &amp;lt;PI,PI,PI&amp;gt; * PI;&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.43&#039;&#039;&#039; ms/statement&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.025&#039;&#039;&#039; ms/statement&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0  // push &#039;this&#039; pointer&lt;br /&gt;
    ldc.r8 (00 00 00 60 fb 21 09 40) // push 64-bit PI (vector x)&lt;br /&gt;
    ldc.r8 (00 00 00 60 fb 21 09 40) // push 64-bit PI (vector y)&lt;br /&gt;
    ldc.r8 (00 00 00 60 fb 21 09 40) // push 64-bit PI (vector z)&lt;br /&gt;
    ldc.r8 (00 00 00 60 fb 21 09 40) // push 64-bit PI multiplicand&lt;br /&gt;
    call class [ScriptTypes]LindenLab.SecondLife.Vector \&lt;br /&gt;
            class [LslUserScript]LindenLab.SecondLife.LslUserScript:: \&lt;br /&gt;
            &#039;CreateVector&#039;(float32, float32, float32)&lt;br /&gt;
    call class [ScriptTypes]LindenLab.SecondLife.Vector \&lt;br /&gt;
            class [LslUserScript]LindenLab.SecondLife.LslUserScript:: \&lt;br /&gt;
            &#039;Multiply&#039;(float32, class [ScriptTypes]LindenLab.SecondLife.Vector)&lt;br /&gt;
    ldc.r8 (00 00 00 60 fb 21 09 40) // push 64-bit PI&lt;br /&gt;
    ldc.r8 (00 00 00 60 fb 21 09 40) // push 64-bit PI&lt;br /&gt;
    ldc.r8 (00 00 00 60 fb 21 09 40) // push 64-bit PI&lt;br /&gt;
    call class [ScriptTypes]LindenLab.SecondLife.Vector \&lt;br /&gt;
            class [LslUserScript]LindenLab.SecondLife.LslUserScript:: \&lt;br /&gt;
            &#039;CreateVector&#039;(float32, float32, float32)&lt;br /&gt;
    call class [ScriptTypes]LindenLab.SecondLife.Vector \&lt;br /&gt;
            class [LslUserScript]LindenLab.SecondLife.LslUserScript:: \&lt;br /&gt;
            &#039;Add&#039;(class [ScriptTypes]LindenLab.SecondLife.Vector, \&lt;br /&gt;
            class [ScriptTypes]LindenLab.SecondLife.Vector)&lt;br /&gt;
    // save result in v:&lt;br /&gt;
    stfld class [ScriptTypes]LindenLab.SecondLife.Vector LSL_script::&#039;v&#039;&lt;br /&gt;
    ldarg.0  // push &#039;this&#039; pointer&lt;br /&gt;
    ldfld class [ScriptTypes]LindenLab.SecondLife.Vector LSL_script::&#039;v&#039;&lt;br /&gt;
    pop&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[llAbs]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llAbs]]();&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.41&#039;&#039;&#039; ms/statement&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.012&#039;&#039;&#039; ms/statement&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldc.i4 0 // push the integer argument&lt;br /&gt;
    call int32 class [LslLibrary]LindenLab.SecondLife.Library::&#039;llAbs&#039;(int32)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: verified and updated for server ver. 1.34.2.142087&lt;br /&gt;
&lt;br /&gt;
===[[Category:LSL_CSV|CSV]] [[string]] conversions===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llCSV2List]]([[llList2CSV]](x));&lt;br /&gt;
where x is a [[list]] of 10 [[integer]]s&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;3.2&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;4 - 7&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0     // push &#039;this&#039; pointer&lt;br /&gt;
    ldfld class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] LSL_script::&#039;x&#039;&lt;br /&gt;
    call string class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llList2CSV&#039;(class [mscorlib]System.Collections.ArrayList)&lt;br /&gt;
    call class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] class \&lt;br /&gt;
            [LslLibrary]LindenLab.SecondLife.Library::&#039;llCSV2List&#039;(string)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: So far, the Mono results vary too much for any meaningful result.&lt;br /&gt;
&lt;br /&gt;
===[[llDetectedName]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llDetectedName]]();&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;1.1&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.8&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldc.i4 0  // push argument&lt;br /&gt;
    call string class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llDetectedName&#039;(int32)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[llGetInventoryName]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llGetInventoryName]](INVENTORY_TEXTURE, 0);&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.52&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.21&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldc.i4 0    // push INVENTORY_TEXTURE&lt;br /&gt;
    ldc.i4 0    // push 2nd argument&lt;br /&gt;
    call string class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llGetInventoryName&#039;(int32, int32)&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[llGetObjectDetails]]===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llGetObjectDetails]](k, [OBJECT_OWNER]);&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;1.3&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;1.7&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0  // push &#039;this&#039; pointer&lt;br /&gt;
    // push k:&lt;br /&gt;
    ldfld valuetype [ScriptTypes]LindenLab.SecondLife.Key LSL_script::&#039;k&#039;&lt;br /&gt;
    ldc.i4 6 // push OBJECT_OWNER&lt;br /&gt;
    box [mscorlib]System.Int32 &lt;br /&gt;
    call class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] \&lt;br /&gt;
            class [LslUserScript]LindenLab.SecondLife.LslUserScript:: \&lt;br /&gt;
            CreateList()&lt;br /&gt;
    call class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] \&lt;br /&gt;
            class [LslUserScript]LindenLab.SecondLife.LslUserScript:: \&lt;br /&gt;
            Prepend(object, class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList])&lt;br /&gt;
    call class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] \&lt;br /&gt;
            class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llGetObjectDetails&#039;(valuetype \&lt;br /&gt;
            [ScriptTypes]LindenLab.SecondLife.Key, \&lt;br /&gt;
            class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: Verified and updated for server ver. 1.34.2.142087&lt;br /&gt;
&lt;br /&gt;
===[[llGetPos]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llGetPos]]();&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.39&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.025&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    call class [ScriptTypes]LindenLab.SecondLife.Vector \&lt;br /&gt;
        class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
        &#039;llGetPos&#039;()&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: Verified and updated for server ver. 1.34.2.142087&lt;br /&gt;
&lt;br /&gt;
===[[llList2Vector]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llList2Vector]](x, 0);&lt;br /&gt;
where x = [ [[ZERO_VECTOR]] ]&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.55&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.012&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0   // push &#039;this&#039; pointer&lt;br /&gt;
    // push list x:&lt;br /&gt;
    ldfld class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] LSL_script::&#039;x&#039;&lt;br /&gt;
    ldc.i4 0  // push 2nd argument: 0&lt;br /&gt;
    call class [ScriptTypes]LindenLab.SecondLife.Vector \&lt;br /&gt;
            class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llList2Vector&#039;(class \&lt;br /&gt;
            [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList], int32)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[llMD5String]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llMD5String]](s,0);&lt;br /&gt;
where s is a 64-char [[string]]&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;1.4&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;1.4&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0                       // push &#039;this&#039; pointer&lt;br /&gt;
    ldfld string LSL_script::&#039;s&#039;  // push string s&lt;br /&gt;
    ldc.i4 0                      // push 2nd arg: 0&lt;br /&gt;
    call string class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llMD5String&#039;(string, int32)&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[llMessageLinked]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=3 width=520|[[llMessageLinked]]([[LINK_THIS]], 0, &amp;quot;&amp;quot;, [[NULL_KEY]]);&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}One consumer script:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;1.5&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.9&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}Two consumer scripts:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;1.8&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;1.2&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}Four consumer scripts:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;2.4&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;1.8&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}Eight consumer scripts:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;4.4&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;3.3&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}16 consumer scripts:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;9 - 20&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;7 - 15&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}32 consumer scripts:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;22.2&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;22.2&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=3|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldc.i4 -4    // push LINK_THIS&lt;br /&gt;
    ldc.i4 0     // push 2nd arg: 0&lt;br /&gt;
    ldstr &amp;quot;&amp;quot;     // push 3rd arg: &amp;quot;&amp;quot;&lt;br /&gt;
    ldstr &amp;quot;00000000-0000-0000-0000-000000000000&amp;quot; // push 4th arg as string&lt;br /&gt;
    // convert that last string to a key type:&lt;br /&gt;
    call valuetype [ScriptTypes]LindenLab.SecondLife.Key \&lt;br /&gt;
            class [LslUserScript]LindenLab.SecondLife.LslUserScript:: \&lt;br /&gt;
            &#039;CreateKey&#039;(string)&lt;br /&gt;
    call void class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llMessageLinked&#039;(int32, int32, string, valuetype \&lt;br /&gt;
            [ScriptTypes]LindenLab.SecondLife.Key)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: The time it takes to call llMessageLinked() is dependent on the number of consumers of the message, up to about 20-30 consumer scripts. At that point, the time to send a link message becomes constant at one call per 22.2 ms, which is suspiciously similar to the sim&#039;s basic frame rate.&lt;br /&gt;
&lt;br /&gt;
===[[llParseString2List]]===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llParseString2List]](s,a,b);&lt;br /&gt;
where s is 2000 random digits, a=[&amp;quot;0&amp;quot;,&amp;quot;1&amp;quot;]; b=[&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;];&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;45&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;24&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0                      // push &#039;this&#039; pointer&lt;br /&gt;
    ldfld string LSL_script::&#039;s&#039; // push 1st arg: s&lt;br /&gt;
    ldarg.0                      // push &#039;this&#039;&lt;br /&gt;
    // push 2nd arg: list a:&lt;br /&gt;
    ldfld class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] LSL_script::&#039;a&#039;&lt;br /&gt;
    ldarg.0                      // push &#039;this&#039;&lt;br /&gt;
    // push 3rd ard: list b:&lt;br /&gt;
    ldfld class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] LSL_script::&#039;b&#039;&lt;br /&gt;
    call class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList] \&lt;br /&gt;
            class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llParseString2List&#039;(string, \&lt;br /&gt;
            class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList], \&lt;br /&gt;
            class [mscorlib][http://www.gnu.org/projects/dotgnu/pnetlib-doc/System/Collections/ArrayList.html System.Collections.ArrayList])&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[llSay]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llSay]](-2, &amp;quot;0123456789&amp;quot;);&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.7&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.2&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldc.i4 2           // push 2...&lt;br /&gt;
    neg                // ...and negate it = -2&lt;br /&gt;
    ldstr &amp;quot;0123456789&amp;quot; // push 2nd arg&lt;br /&gt;
    call void class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llSay&#039;(int32, string)&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes:&lt;br /&gt;
:#The performance of llSay(-2, &amp;quot;0123456789&amp;quot;) varies widely by region, although it can be pretty consistent within a region. In some nearly empty regions, llSay() slows to no better than 5 ms/call for reasons unknown. It&#039;s as if the throttling for this function is less deterministic than other LSL functions. But even in the worst case, llSay() can transmit faster than llListen() can receive.&lt;br /&gt;
:#Verified and updated for server ver. 1.34.2.142087.&lt;br /&gt;
&lt;br /&gt;
===[[llSetLinkAlpha]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llSetLinkAlpha]](link, alpha, side);&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;1.0&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.52&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldc.i4 1                         // push 1st arg: link number&lt;br /&gt;
    ldc.r8 (00 00 00 60 b8 1e d5 3f) // push 2nd arg: float alpha&lt;br /&gt;
    ldc.i4 0                         // push 3rd arg: side number&lt;br /&gt;
    call void class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llSetLinkAlpha&#039;(int32, float32, int32)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[llSetText]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llSetText]](s,c,a);&lt;br /&gt;
where [[string]] s is 20 chars&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;0.79&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;0.38&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0                       // push &#039;this&#039; pointer&lt;br /&gt;
    ldfld string LSL_script::&#039;s&#039;  // push 1st arg: string s&lt;br /&gt;
    ldarg.0                       // push &#039;this&#039;&lt;br /&gt;
    // push 2nd arg: color:&lt;br /&gt;
    ldfld class [ScriptTypes]LindenLab.SecondLife.Vector LSL_script::&#039;c&#039;&lt;br /&gt;
    ldarg.0                       // push &#039;this&#039;&lt;br /&gt;
    ldfld float32 LSL_script::&#039;a&#039; // push 3rd arg: a&lt;br /&gt;
    call void class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llSetText&#039;(string, \&lt;br /&gt;
            class [ScriptTypes]LindenLab.SecondLife.Vector, float32)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[llSubStringIndex]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|[[llSubStringIndex]](s, &amp;quot;X&amp;quot;);&lt;br /&gt;
where s is 10000 digits not containing &amp;quot;X&amp;quot;&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;16&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|*Mono: &#039;&#039;&#039;22&#039;&#039;&#039; ms/call&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}colspan=2|*Mono CIL:&lt;br /&gt;
:&amp;lt;code style=&amp;quot;font-size:90%&amp;quot;&amp;gt;&lt;br /&gt;
    ldarg.0                      // push &#039;this&#039; pointer&lt;br /&gt;
    ldfld string LSL_script::&#039;s&#039; // push 1st arg: string a&lt;br /&gt;
    ldstr &amp;quot;X&amp;quot;                    // push 2nd arg: literal string&lt;br /&gt;
    call int32 class [LslLibrary]LindenLab.SecondLife.Library:: \&lt;br /&gt;
            &#039;llSubStringIndex&#039;(string, string)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
== Event handlers ==&lt;br /&gt;
&lt;br /&gt;
===[[dataserver]]() for [[llGetNotecardLine]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|Maximum rate of [[dataserver]]() events using [[llGetNotecardLine]]()&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;6.4&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;6.4&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[link_message]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|Maximum rate of [[link_message]]() events&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;42-45&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;42-45&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: The results were highly variable over several tens of millions of link_message() events in three lightly loaded sims, but I wonder if the maximum rate is tied to the sim frame rate of 45 fps.&lt;br /&gt;
&lt;br /&gt;
===[[listen]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=3 width=520|Maximum rate of [[listen]]() events&lt;br /&gt;
any number of scripts per prim, any number of prims in linkset&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}1 listener per script:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;14.7&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;14.7&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}Two listeners per script:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;11&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;11&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}Three listeners per script:&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;8.9&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;8.9&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}}&lt;br /&gt;
&lt;br /&gt;
===[[sensor]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|Maximum rate of [[sensor]]() events&lt;br /&gt;
after [[llSensorRepeat]]()&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;15&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;22&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: The maximum rate of sensor() events seems to be independent of the sensor radius or number of things detected.&lt;br /&gt;
&lt;br /&gt;
===[[timer]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|Maximum rate of [[timer]]() events&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;22&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;22&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: About half the sim&#039;s basic frame rate of 45 fps.&lt;br /&gt;
&lt;br /&gt;
===[[touch]]()===&lt;br /&gt;
:{{{!}} {{Prettytable|style=margin-top:0; font-size:100%}}&lt;br /&gt;
{{!}}-&lt;br /&gt;
!colspan=2 width=520|Maximum rate of [[touch]]() events&lt;br /&gt;
{{!}}-&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|LSL: &#039;&#039;&#039;22&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}align=&amp;quot;center&amp;quot;|Mono: &#039;&#039;&#039;22&#039;&#039;&#039; events/sec&lt;br /&gt;
{{!}}}&lt;br /&gt;
:Notes: About half the sim&#039;s basic frame rate of 45 fps.&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User_talk:Becky_Pippen/New_LSL_Functions&amp;diff=712152</id>
		<title>User talk:Becky Pippen/New LSL Functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User_talk:Becky_Pippen/New_LSL_Functions&amp;diff=712152"/>
		<updated>2010-01-24T23:05:21Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Thanks for providing the info. The suggested relative resizing troubles me, since it accumulates error that breaks objects (besides this, no recovery when the position component fails due to not-completely-predictable SL behavior). For objects that only resize a few times, like most jewelry, the error does not grow enough to be noticed, and my concern is more principle than practical. But, I make some objects that resize many times, for example, as part of RP or in response to nearby AV actions, so they use absolute resizing. (Even for jewelry, I use absolute resizing, with a single-script resizer for low script time, but that stores original sizes and positions in memory. They can be re-read from a notecard in case of a script reset, but not during normal operation since that is so slow.) [[User:Xoph Adamczyk|Xoph Adamczyk]] 16:55, 22 January 2010 (UTC)&lt;br /&gt;
:Thanks, Xoph, you&#039;re quite right about the risks of repositioning this way, and I&#039;m glad you pointed it out. The main point of the code snippets is to suggest a general loop structure for getting and setting parameters in all the prims in a linkset, not the specific parameter changes which need to be carefully considered for each product&#039;s use cases. I&#039;d love it if someone contributed a more realistic example for a specific application. [[User:Becky Pippen|Becky Pippen]] 18:03, 22 January 2010 (UTC)&lt;br /&gt;
&lt;br /&gt;
== When the time comes... ==&lt;br /&gt;
&lt;br /&gt;
When the time comes that these functions go live, would you be alright with some of what you have written being used in/as the documentation? -- &#039;&#039;&#039;[[User:Strife_Onizuka|Strife]]&#039;&#039;&#039; &amp;lt;sup&amp;gt;&amp;lt;small&amp;gt;([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])&amp;lt;/small&amp;gt;&amp;lt;/sup&amp;gt; 22:03, 24 January 2010 (UTC)&lt;br /&gt;
:Sure :-) [[User:Becky Pippen|Becky Pippen]] 23:05, 24 January 2010 (UTC)&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User_talk:Becky_Pippen/New_LSL_Functions&amp;diff=711623</id>
		<title>User talk:Becky Pippen/New LSL Functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User_talk:Becky_Pippen/New_LSL_Functions&amp;diff=711623"/>
		<updated>2010-01-22T18:03:58Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Thanks for providing the info. The suggested relative resizing troubles me, since it accumulates error that breaks objects (besides this, no recovery when the position component fails due to not-completely-predictable SL behavior). For objects that only resize a few times, like most jewelry, the error does not grow enough to be noticed, and my concern is more principle than practical. But, I make some objects that resize many times, for example, as part of RP or in response to nearby AV actions, so they use absolute resizing. (Even for jewelry, I use absolute resizing, with a single-script resizer for low script time, but that stores original sizes and positions in memory. They can be re-read from a notecard in case of a script reset, but not during normal operation since that is so slow.) [[User:Xoph Adamczyk|Xoph Adamczyk]] 16:55, 22 January 2010 (UTC)&lt;br /&gt;
:Thanks, Xoph, you&#039;re quite right about the risks of repositioning this way, and I&#039;m glad you pointed it out. The main point of the code snippets is to suggest a general loop structure for getting and setting parameters in all the prims in a linkset, not the specific parameter changes which need to be carefully considered for each product&#039;s use cases. I&#039;d love it if someone contributed a more realistic example for a specific application. [[User:Becky Pippen|Becky Pippen]] 18:03, 22 January 2010 (UTC)&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/New_LSL_Functions&amp;diff=709472</id>
		<title>User:Becky Pippen/New LSL Functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/New_LSL_Functions&amp;diff=709472"/>
		<updated>2010-01-20T18:30:42Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: added minor updates from today&amp;#039;s office hours&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== New LSL Functions ==&lt;br /&gt;
&lt;br /&gt;
The information in this section is preliminary and predictive, based on information given by&lt;br /&gt;
[[Babbage_Linden|Babbage Linden at his Office Hours]].&lt;br /&gt;
&lt;br /&gt;
To help reduce the number of scripts needed for certain common operations, we will have these new&lt;br /&gt;
LSL functions, perhaps as early as server version 1.38:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; list llGetLinkPrimitiveParams(integer linknumber, list params)&lt;br /&gt;
 &lt;br /&gt;
      llSetLinkPrimitiveParamsFast(integer link, list params) &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will also get PRIM_TEXT for getting and setting hover text with [[llSetLinkPrimitiveParams]]().&lt;br /&gt;
The parameter list will be:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; PRIM_TEXT, string msg, vector color, float alpha &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===History===&lt;br /&gt;
&lt;br /&gt;
Some content creators prefer to distribute their objects no-mod, yet still allow customers to resize or&lt;br /&gt;
retexture the linkset. Scripts can do that in a no-mod linkset, but the obvious solution using&lt;br /&gt;
[[llSetLinkPrimitiveParams]]() doesn&#039;t work well because of the 0.2-second delay built into the function.&lt;br /&gt;
To work around the delay, we have had to put a script in every child prim that can respond immediately&lt;br /&gt;
to a message from the root prim. When these functions become available, we will be able to do this kind of thing with a single script.&lt;br /&gt;
&lt;br /&gt;
===Fixing Existing Scripts===&lt;br /&gt;
&lt;br /&gt;
To fix such a script, locate the call to [[llMessageLinked]]() in the master script where it sends the link&lt;br /&gt;
message to the individual prims. It might look something vaguely like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llMessageLinked(LINK_whatever, command_resize, (string)newSize, NULL_KEY);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Replace that with a loop that applies whatever changes you need to each prim using&lt;br /&gt;
[[llSetLinkPrimitiveParamsFast]](). The prims have link numbers from 1 through [[llGetNumberOfPrims]](),&lt;br /&gt;
so you can make an efficient loop as shown here; just add any additional processing you&lt;br /&gt;
need for each prim inside the loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; applyChildParams(list ruleSet)&lt;br /&gt;
 {&lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum, ruleSet);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, to use this function to set the color and alpha of all the prims, build the parameter&lt;br /&gt;
list and pass it to applyChildParams() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;applyChildParams([PRIM_COLOR, ALL_SIDES, &amp;lt;0.9, 0.8, 0.2&amp;gt;, 1.0]);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
However, when you only need to apply a constant rule set like this to every prim in the linkset,&lt;br /&gt;
then the loop above can be simplified to a single statement;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llSetLinkPrimitiveParamsFast(LINK_SET, ruleSet);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or use [[LINK_ALL_OTHERS]] or [[LINK_ALL_CHILDREN]] instead of [[LINK_SET]] as appropriate.&lt;br /&gt;
&lt;br /&gt;
For another example, you can make a resizer script apply relative instead of absolute size changes by&lt;br /&gt;
using the new function [[llGetLinkPrimitiveParams]](). The script can read each child&#039;s current size,&lt;br /&gt;
then apply a resize factor and use [[llSetLinkPrimitiveParamsFast]]() to set a new size. This can help&lt;br /&gt;
make it easy for the script to do the right thing even if reset and all its variables get reinitialized. The&lt;br /&gt;
following function will resize all the prims (including the root itself) by a factor:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; rescaleLinksetByFactor(float factor)&lt;br /&gt;
 {&lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list params = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE]);&lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum,&lt;br /&gt;
             [PRIM_SIZE, factor * llList2Vector(params, 0)]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But what if the function above encounters a prim that, after resized, would have a dimension outside&lt;br /&gt;
the legal range of 0.01m to 10.0m? That could have unfortunate consequences, so&lt;br /&gt;
here is a function that resizes all the prims by a specified factor but only if all the resulting&lt;br /&gt;
dimensions in all the prims will all be in the legal range of 0.01m to 10.0m. If any dimension in&lt;br /&gt;
any prim would go out of those bounds, the function will return FALSE without resizing anything.&lt;br /&gt;
This example also includes repositioning the child prims within the linkset. Change or add whatever&lt;br /&gt;
additional parameters are needed for your particular application.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // This is not a complete resizer solution; it&#039;s just a demo of&lt;br /&gt;
 // how to call the new LSL functions.&lt;br /&gt;
 &lt;br /&gt;
 // Set the minimum and maximum size allowed for any prim in the linkset:&lt;br /&gt;
 //&lt;br /&gt;
 float minPrimSize = 0.01;&lt;br /&gt;
 float maxPrimSize = 10.0;&lt;br /&gt;
 &lt;br /&gt;
 // Resize all the prims in the linkset. For example, to resize all&lt;br /&gt;
 // the prims to 1% smaller, call this function with factor=0.99&lt;br /&gt;
 // Returns TRUE if successful, or FALSE if resizing would have&lt;br /&gt;
 // set a prim size out of bounds.&lt;br /&gt;
 //&lt;br /&gt;
 integer resizeLinksetWithBounds(float factor)&lt;br /&gt;
 {&lt;br /&gt;
     // Test to see if it&#039;s safe to change all the prim sizes:&lt;br /&gt;
 &lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list params = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE]);&lt;br /&gt;
         vector sz = factor * llList2Vector(params, 0);&lt;br /&gt;
         if (sz.x &amp;lt; minPrimSize || sz.x &amp;gt; maxPrimSize ||&lt;br /&gt;
             sz.y &amp;lt; minPrimSize || sz.y &amp;gt; maxPrimSize ||&lt;br /&gt;
             sz.z &amp;lt; minPrimSize || sz.z &amp;gt; maxPrimSize) {&lt;br /&gt;
                 return FALSE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     // We&#039;ve determined that we can safely change all the sizes, so&lt;br /&gt;
     // let&#039;s do so now. This example assumes that llGetLinkPrimitiveParams()&lt;br /&gt;
     // with PRIM_POSITION will return the relative offset from the root prim,&lt;br /&gt;
     // but those details have not yet been published.&lt;br /&gt;
 &lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list oldParams = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE, PRIM_POSITION]);&lt;br /&gt;
 &lt;br /&gt;
         list newParams = [ PRIM_SIZE, factor * llList2Vector(oldParams, 0) ];&lt;br /&gt;
         if (linkNum &amp;gt; 1) {&lt;br /&gt;
             newParams += [ PRIM_POSITION, factor * llList2Vector(oldParams, 1) ];&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum, newParams);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return TRUE;&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&#039;s a silly example of how it&#039;s used:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Example of how to call resizeLinksetWithBounds()&lt;br /&gt;
 //&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         float factor = llPow(llFrand(2.0), 2.0); // random factor for demo&lt;br /&gt;
         if (resizeLinksetWithBounds(factor)) {&lt;br /&gt;
             llOwnerSay(&amp;quot;Resized successfully by factor = &amp;quot; + (string)factor);&lt;br /&gt;
         } else {&lt;br /&gt;
             llOwnerSay(&amp;quot;Oops, can&#039;t resize by factor &amp;quot; + (string)factor);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Long range plans===&lt;br /&gt;
&lt;br /&gt;
The new LSL features described above are expected to appear in server version 1.38. Babbage Linden also mentioned a few additional LSL features he would like to implement sometime after 1.38, but with no guarantees if or when:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; lLinkParticleSystem()&lt;br /&gt;
 PRIM_NAME, PRIM_DESC  // for llGet/SetPrimitiveParams()&lt;br /&gt;
 llAvatarOnLinkSitTarget()&lt;br /&gt;
 llLinkSitTarget() &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Also see===&lt;br /&gt;
&lt;br /&gt;
* [[User:Becky_Pippen/Script_Memory_Limits| Scripter&#039;s checklist of things you can do to conserve memory]]&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Limits_FAQ| Script memory limits FAQ]]&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Memory_Limits_FAQ&amp;diff=706042</id>
		<title>User:Becky Pippen/Memory Limits FAQ</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Memory_Limits_FAQ&amp;diff=706042"/>
		<updated>2010-01-16T21:47:37Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: Added Q: Will the limits be per avatar or per region?&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== FAQ - Script Memory Limits ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Why do we need script memory limits?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; In some regions, the simulator uses so much script memory that it has to swap virtual memory to disk. That causes major lag, and lag is among the top complaints about Second Life.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; How can I tell if I&#039;m a heavy script user?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; There are two ways: According to Babbage Linden, we&#039;ll have new ways built into the sim and viewer to see our individual script memory usage, similar to how estate managers can see top scripts in a region. Also scripters can use the function [[LlGetFreeMemory|llGetFreeMemory()]] to get an approximate idea of memory usage.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will this break content?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Yes, some. The limits are specifically designed to limit the worst 5% of memory consumers so that the remaining 95% can continue to run freely without causing the simulator to swap virtual memory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; When will all this happen?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; At some time around the first quarter of 2010, the Linden engineers will have figured out the exact limits and policies. After the official announcements, we&#039;ll have several months where we can experiment with the new LSL functions and tools on the preview grid, then on the main grid before the limits are enforced sometime later in 2010.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will the limits be per avatar or per region?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; That hasn&#039;t been determined yet, but it will probably involve limits on avatar, parcel, and region in a way that parallels [[LSL_http_server#Resource_Limitations|how URL resources are managed]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will Linden Lab pay for all the scripted products we bought that will no longer work?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Most products are reasonably scripted and will continue to work just fine. The only problematic products will be unusual ones that are scripted in ways that use unreasonable amounts of RAM. Try to get the product creator to make a new version that uses less memory.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Will we see a reduction in lag across the grid?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; It won&#039;t have a major impact on most regions. However, it will make a huge improvement on the 5% of the regions that use so much script memory that the server has to swap virtual memory to disk. Theoretically it might also somewhat improve performance on any other simulators running on the same server host using the same disk resources.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; How much RAM are we talking about?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; In the 95% of the regions that run smoothly, the simulator uses less than 800MB of RAM for everything except scripts, and up to 300MB for all the scripts in the region, and that&#039;s ok.  In the worst 5% of the regions, simulators use up to 2GB of RAM on the server, and that&#039;s not ok.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Why not just add RAM to the servers? RAM is cheap.&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Several thousand Xeon server boards at, say, US$100 each to upgrade, comes out to a bunch of money, even for Linden Lab. Somebody would have to pay for that, and it wouldn&#039;t be from residents with free accounts.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; Who will be affected?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; All scripts, old and new, will be subject to the new memory limits. When planning for the memory limits, Babbage Linden said, &amp;quot;[2009/11/25 3:43]  Babbage Linden: ideally we&#039;d like to have 95+% of people use scripts as they currently do while still having 95+% of simulators running without swapping&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What can I do as a content creator to prepare?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; Be kind to your customers and give them product updates using the [[User:Becky_Pippen/New_LSL_Functions| new efficient LSL functions]] when they become available. Learn more about how to [[User:Becky_Pippen/Measure_Script_Memory_Usage|measure memory usage]] and the [[User:Becky_Pippen/Script_Memory_Limits|scripting techniques for reducing memory]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Q:&#039;&#039;&#039; What can I do as a customer?&lt;br /&gt;
: &#039;&#039;&#039;A:&#039;&#039;&#039; 95% of scripts are just fine and you don&#039;t need to do anything. If you are wearing hair, shoes, or other attachments with a script in every prim, then you can contact the creator and ask if there is a product update that uses the new LSL memory-efficient functions.&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/New_LSL_Functions&amp;diff=704422</id>
		<title>User:Becky Pippen/New LSL Functions</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/New_LSL_Functions&amp;diff=704422"/>
		<updated>2010-01-14T04:11:03Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: minor clarifications&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== New LSL Functions ==&lt;br /&gt;
&lt;br /&gt;
The information in this section is preliminary and predictive, based on information given by&lt;br /&gt;
Babbage Linden at his Office Hours.&lt;br /&gt;
&lt;br /&gt;
To help reduce the number of scripts needed for certain common operations, we will have these new&lt;br /&gt;
LSL functions, perhaps as early as server version 1.38:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; llGetLinkPrimitiveParams()&lt;br /&gt;
 llSetLinkPrimitiveParamsFast() &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And possibly a new parameter ID for setting hover text with llSetLinkPrimitiveParamsFast():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; PRIM_TEXT &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===History===&lt;br /&gt;
&lt;br /&gt;
Some content creators prefer to distribute their objects no-mod, yet still allow customers to resize or&lt;br /&gt;
retexture the linkset. Scripts can do that in a no-mod linkset, but the obvious solution using&lt;br /&gt;
[[llSetLinkPrimitiveParams]]() doesn&#039;t work well because of the 0.2-second delay built into the function.&lt;br /&gt;
To work around the delay, we have had to put a script in every child prim that can respond immediately&lt;br /&gt;
to a message from the root prim. When these functions become available, we will be able to do this kind of thing with a single script.&lt;br /&gt;
&lt;br /&gt;
===Fixing Existing Scripts===&lt;br /&gt;
&lt;br /&gt;
To fix such a script, locate the call to [[llMessageLinked]]() in the master script where it sends the link&lt;br /&gt;
message to the individual prims. It might look something vaguely like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llMessageLinked(LINK_whatever, command_resize, (string)newSize, NULL_KEY);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Replace that with a loop that applies whatever changes you need to each prim using&lt;br /&gt;
[[llSetLinkPrimitiveParamsFast]](). The prims have link numbers from 1 through [[llGetNumberOfPrims]](),&lt;br /&gt;
so you can make an efficient loop as shown here; just add any additional processing you&lt;br /&gt;
need for each prim inside the loop:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; applyChildParams(list ruleSet)&lt;br /&gt;
 {&lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum, ruleSet);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example, to use this function to set the color and alpha of all the prims, build the parameter&lt;br /&gt;
list and pass it to applyChildParams() like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;applyChildParams([PRIM_COLOR, ALL_SIDES, &amp;lt;0.9, 0.8, 0.2&amp;gt;, 1.0]);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
However, when you only need to apply a constant rule set like this to every prim in the linkset,&lt;br /&gt;
then the loop above can be simplified to a single statement;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llSetLinkPrimitiveParamsFast(LINK_SET, ruleSet);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or use [[LINK_ALL_OTHERS]] or [[LINK_ALL_CHILDREN]] instead of [[LINK_SET]] as appropriate.&lt;br /&gt;
&lt;br /&gt;
For another example, you can make a resizer script apply relative instead of absolute size changes by&lt;br /&gt;
using the new function [[llGetLinkPrimitiveParams]](). The script can read each child&#039;s current size,&lt;br /&gt;
then apply a resize factor and use [[llSetLinkPrimitiveParamsFast]]() to set a new size. This can help&lt;br /&gt;
make it easy for the script to do the right thing even if reset and all its variables get reinitialized. The&lt;br /&gt;
following function will resize all the prims (including the root itself) by a factor:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; rescaleLinksetByFactor(float factor)&lt;br /&gt;
 {&lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list params = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE]);&lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum,&lt;br /&gt;
             [PRIM_SIZE, factor * llList2Vector(params, 0)]);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But what if the function above encounters a prim that, after resized, would have a dimension outside&lt;br /&gt;
the legal range of 0.01m to 10.0m? That could have unfortunate consequences, so&lt;br /&gt;
here is a function that resizes all the prims by a specified factor but only if all the resulting&lt;br /&gt;
dimensions in all the prims will all be in the legal range of 0.01m to 10.0m. If any dimension in&lt;br /&gt;
any prim would go out of those bounds, the function will return FALSE without resizing anything.&lt;br /&gt;
This example also includes repositioning the child prims within the linkset. Change or add whatever&lt;br /&gt;
additional parameters are needed for your particular application.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // This is not a complete resizer solution; it&#039;s just a demo of&lt;br /&gt;
 // how to call the new LSL functions.&lt;br /&gt;
 &lt;br /&gt;
 // Set the minimum and maximum size allowed for any prim in the linkset:&lt;br /&gt;
 //&lt;br /&gt;
 float minPrimSize = 0.01;&lt;br /&gt;
 float maxPrimSize = 10.0;&lt;br /&gt;
 &lt;br /&gt;
 // Resize all the prims in the linkset. For example, to resize all&lt;br /&gt;
 // the prims to 1% smaller, call this function with factor=0.99&lt;br /&gt;
 // Returns TRUE if successful, or FALSE if resizing would have&lt;br /&gt;
 // set a prim size out of bounds.&lt;br /&gt;
 //&lt;br /&gt;
 integer resizeLinksetWithBounds(float factor)&lt;br /&gt;
 {&lt;br /&gt;
     // Test to see if it&#039;s safe to change all the prim sizes:&lt;br /&gt;
 &lt;br /&gt;
     integer linkNum;&lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list params = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE]);&lt;br /&gt;
         vector sz = factor * llList2Vector(params, 0);&lt;br /&gt;
         if (sz.x &amp;lt; minPrimSize || sz.x &amp;gt; maxPrimSize ||&lt;br /&gt;
             sz.y &amp;lt; minPrimSize || sz.y &amp;gt; maxPrimSize ||&lt;br /&gt;
             sz.z &amp;lt; minPrimSize || sz.z &amp;gt; maxPrimSize) {&lt;br /&gt;
                 return FALSE;&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     // We&#039;ve determined that we can safely change all the sizes, so&lt;br /&gt;
     // let&#039;s do so now. This example assumes that llGetLinkPrimitiveParams()&lt;br /&gt;
     // with PRIM_POSITION will return the relative offset from the root prim,&lt;br /&gt;
     // but those details have not yet been published.&lt;br /&gt;
 &lt;br /&gt;
     for (linkNum = llGetNumberOfPrims(); linkNum &amp;gt;= 1; --linkNum) {&lt;br /&gt;
         list oldParams = llGetLinkPrimitiveParams(linkNum, [PRIM_SIZE, PRIM_POSITION]);&lt;br /&gt;
 &lt;br /&gt;
         list newParams = [ PRIM_SIZE, factor * llList2Vector(oldParams, 0) ];&lt;br /&gt;
         if (linkNum &amp;gt; 1) {&lt;br /&gt;
             newParams += [ PRIM_POSITION, factor * llList2Vector(oldParams, 1) ];&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         llSetLinkPrimitiveParamsFast(linkNum, newParams);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return TRUE;&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here&#039;s a silly example of how it&#039;s used:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Example of how to call resizeLinksetWithBounds()&lt;br /&gt;
 //&lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         float factor = llPow(llFrand(2.0), 2.0); // random factor for demo&lt;br /&gt;
         if (resizeLinksetWithBounds(factor)) {&lt;br /&gt;
             llOwnerSay(&amp;quot;Resized successfully by factor = &amp;quot; + (string)factor);&lt;br /&gt;
         } else {&lt;br /&gt;
             llOwnerSay(&amp;quot;Oops, can&#039;t resize by factor &amp;quot; + (string)factor);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Also see===&lt;br /&gt;
&lt;br /&gt;
* [[User:Becky_Pippen/Script_Memory_Limits| Scripter&#039;s checklist of things you can do to conserve memory]]&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Limits_FAQ| Script memory limits FAQ]]&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Text_Storage&amp;diff=701383</id>
		<title>User:Becky Pippen/Text Storage</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Text_Storage&amp;diff=701383"/>
		<updated>2010-01-11T19:43:43Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: minor tweaks&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Text strings in scripts ==&lt;br /&gt;
&lt;br /&gt;
===Terminology===&lt;br /&gt;
&lt;br /&gt;
Be warned — these terms are not always used correctly and consistently in the LSL wikis. Here&#039;s the terminology from their various standards:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;[http://en.wikipedia.org/wiki/Unicode Unicode]&#039;&#039;&#039; is a character set of several thousand character glyphs and their assigned numeric ID codes. The numeric codes are 21-bit integers in the range 0 through 0x10ffff, although a few numbers in that range have special meaning other than character data. How are those 21-bit numbers stored in memory? That&#039;s what UTF-8 and UTF-16 are all about.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;[http://en.wikipedia.org/wiki/Utf-8 UTF-8]&#039;&#039;&#039; and &#039;&#039;&#039;[http://en.wikipedia.org/wiki/Utf-16 UTF-16]&#039;&#039;&#039; are two ways to represent the Unicode numeric codes in memory. UTF-8 encodes a Unicode ID number in a variable-length sequence of one to four bytes. UTF-16 encodes most characters in 16 bits, and some in 32 bits.&lt;br /&gt;
&lt;br /&gt;
For a summary of Unicode character set and examples of the UTF-8 and UTF-16 storage formats, see: [[Unicode_In_5_Minutes]].&lt;br /&gt;
&lt;br /&gt;
LSO-compiled scripts store strings internally in memory in UTF-8 format and Mono uses UTF-16, but all that should be transparent to the script for most purposes. The main impact for the script and scripter is the amount of memory used. [[LSL_Script_Memory|This page]] says that globally scoped strings use one byte per character in LSO and two bytes per character in Mono, which is true for strings containing only 7-bit ASCII characters. When the strings contain mostly international characters outside the ASCII range, then the memory consumption for UTF-8 in LSO and UTF-16 in Mono will both be close to two bytes per character, and in some cases the UTF-8 will be longer if there are many characters that require the three-byte or four-byte forms of UTF-8 encoding.&lt;br /&gt;
&lt;br /&gt;
===So, summarize, ok?===&lt;br /&gt;
&lt;br /&gt;
So, if using only an ASCII character set and compiling with Mono, we can possibly reduce the memory requirements to near LSO levels through clever encoding. Otherwise any attempt made to compress text will probably use more instruction code space than the amount of memory saved.&lt;br /&gt;
&lt;br /&gt;
===ASCII text compression in Mono===&lt;br /&gt;
&lt;br /&gt;
This technique applies if your script uses only ASCII characters and only if compiling with Mono. We&#039;ll take the ASCII characters, two at a time, convert them to their Unicode numeric ID codes (which for these characters are identical to their ASCII numeric codes), combine them into a 14-bit integer, add a bias that so that the 14-bit numbers all are inside a valid range of Unicode characters, then convert it into a single Unicode character. This will result in a 16-bit Unicode character that bears no resemblance to the two ASCII characters that it encodes.&lt;br /&gt;
&lt;br /&gt;
The decoding process is just the reverse — convert a Unicode character into its numeric value which will be a 14-bit integer, divide it into two 7-bit numbers, and convert that back to a string of two characters.&lt;br /&gt;
&lt;br /&gt;
For encoding, we need a couple of functions — one to convert a single Unicode character to its numeric ID, and one to convert a 14-bit number into a single Unicode character. Here&#039;s the former:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Given a single character c, this returns its Unicode ID number&lt;br /&gt;
 // This works only for character codes 0 through 0xffff.&lt;br /&gt;
 //&lt;br /&gt;
 integer charToUnicodeIdNumber(string c)&lt;br /&gt;
     integer cInt = llBase64ToInteger(llStringToBase64(c));&lt;br /&gt;
 &lt;br /&gt;
     if (!(cInt &amp;amp; 0x80000000)) {&lt;br /&gt;
         // UTF-8 single-byte form&lt;br /&gt;
         cInt = cInt &amp;gt;&amp;gt; 24;&lt;br /&gt;
     } else {&lt;br /&gt;
         if ((cInt &amp;amp; 0xe0000000) == 0xc0000000) {&lt;br /&gt;
             // two-byte UTF-8 form:  110v vvvv  10vv vvvv&lt;br /&gt;
             cInt = ((cInt &amp;amp; 0x1f000000) &amp;gt;&amp;gt; 18) |&lt;br /&gt;
                    ((cInt &amp;amp; 0x003f0000) &amp;gt;&amp;gt; 16);&lt;br /&gt;
         } else {&lt;br /&gt;
             // assume three-byte UTF-8 form:  1110 vvvv  10vv vvvv  10vv vvvv&lt;br /&gt;
             cInt = ((cInt &amp;amp; 0x0f000000) &amp;gt;&amp;gt; 12) |&lt;br /&gt;
                    ((cInt &amp;amp; 0x003f0000) &amp;gt;&amp;gt; 10) |&lt;br /&gt;
                    ((cInt &amp;amp; 0x00003f00) &amp;gt;&amp;gt; 8);&lt;br /&gt;
         } // else ignore the 4-byte UTF-8 form&lt;br /&gt;
     } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This works because the function [[llStringToBase64]]() converts the character c into UTF-8 format first, then into base64 encoding. This isn&#039;t documented well, so let&#039;s be clear about it — regardless if running in LSO where text is UTF-8 or in Mono where text is UTF-16, the function [[llStringToBase64]]() will return a base64-encoded form of the UTF-8 encoding of its argument.&lt;br /&gt;
&lt;br /&gt;
This function is very similar to the function&lt;br /&gt;
&amp;lt;lsl&amp;gt; integer UTF8ToUnicodeInteger(string input); &amp;lt;/lsl&amp;gt;&lt;br /&gt;
found in [[Combined_Library]]. I&#039;m offering my own version here for two reasons: (1) I&#039;ve named the function to emphasize that it takes a single character as string input, and to remove &amp;quot;UTF-8&amp;quot; from the function name because the role of UTF-8 in the function is only as a necessary intermediate encoding. The function is meant to work with LSO or Mono so that the underlying internal encoding of the input argument is transparent; and (2) my version is a little easier to read (at the expense of being a few bytes longer).&lt;br /&gt;
&lt;br /&gt;
And just for completeness, if you need to revise the function to also work with Unicode ID codes above 0xffff, the additional else clause looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; . . .&lt;br /&gt;
 } else {&lt;br /&gt;
     // four-byte UTF-8 form:  1111 0vvv 10vv vvvv 10vv vvvv 10vv vvvv&lt;br /&gt;
     cInt = ((cInt &amp;amp; 0x07000000) &amp;gt;&amp;gt; 6) |&lt;br /&gt;
            ((cInt &amp;amp; 0x003f0000) &amp;gt;&amp;gt; 4) |&lt;br /&gt;
            ((cInt &amp;amp; 0x00003f00) &amp;gt;&amp;gt; 2) |&lt;br /&gt;
             (cInt &amp;amp; 0x0000003f);&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next, we need a way to combine two 7-bit character codes into a single Unicode character. We&#039;ll simply combine two numbers to make a 14-bit integer, then convert that to a Unicode character using the function encode15BitsToChar() found in [[User:Becky_Pippen/Numeric_Storage]]:&lt;br /&gt;
&lt;br /&gt;
For decoding, we&#039;ll use the function decodeCharTo15Bits() found in [[User:Becky_Pippen/Numeric_Storage]] to get our 14-bit number, then split that into two 7-bit numbers, then use [[llUnescapeURL]]() to turn those into two characters in a string.&lt;br /&gt;
&lt;br /&gt;
===Code Example===&lt;br /&gt;
&lt;br /&gt;
First, we need to run a benchmark to measure memory usage in the normal uncompressed way. Simply put a bunch of ASCII text into a notecard and drop it into a prim with this script. It will concatenate all the notecard lines into a single global string named bigText. I used the text from http://www.gutenberg.org/files/6274/6274.txt as the notecard text. It runs out of memory after saving 1063 notecard lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
 integer lineNumber;&lt;br /&gt;
 string notecardName = &amp;quot;ASCII text&amp;quot;;&lt;br /&gt;
 integer linesRead;&lt;br /&gt;
 string bigText;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         llGetNotecardLine(notecardName, lineNumber++);&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     dataserver(key id, string data)&lt;br /&gt;
     {&lt;br /&gt;
         if (data != EOF) {&lt;br /&gt;
             bigText += data;&lt;br /&gt;
             llOwnerSay((string)(++linesRead) + &amp;quot;:&amp;quot; + data);&lt;br /&gt;
             llGetNotecardLine(notecardName, lineNumber++);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we can compare — here&#039;s the same notecard reader script using ASCII compression. Even though the added compression functions consume about 3K of program space, we can now store 1854 lines. &#039;&#039;&#039;That&#039;s 74% more text using compression.&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Demo of ASCII compression in Mono scripts&lt;br /&gt;
 // By Becky Pippen, 2009, contributed to Public Domain&lt;br /&gt;
 &lt;br /&gt;
 // Report memory free and the change from the last report:&lt;br /&gt;
 //&lt;br /&gt;
 integer lastFree;&lt;br /&gt;
 &lt;br /&gt;
 mem(string label)&lt;br /&gt;
 {&lt;br /&gt;
     integer newFree = llGetFreeMemory();&lt;br /&gt;
     integer change = lastFree - newFree;&lt;br /&gt;
     llOwnerSay((string)newFree + &amp;quot; free, change = &amp;quot; + (string)change);&lt;br /&gt;
     lastFree = newFree;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Converts n = [0..0xff] to two hex characters&lt;br /&gt;
 //&lt;br /&gt;
 string hexChar2(integer n)&lt;br /&gt;
 {&lt;br /&gt;
     string hexChars = &amp;quot;0123456789abcdef&amp;quot;;&lt;br /&gt;
     return llGetSubString(hexChars, n &amp;gt;&amp;gt; 4, n &amp;gt;&amp;gt; 4) +&lt;br /&gt;
            llGetSubString(hexChars, n &amp;amp; 0xf, n &amp;amp; 0xf);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Given a single character c, this returns its Unicode ID number&lt;br /&gt;
 // This works only for character codes 0 through 0xffff.&lt;br /&gt;
 // For a more compact alternative, see UTF8ToUnicodeInteger()&lt;br /&gt;
 // found in http://wiki.secondlife.com/wiki/Combined_Library .&lt;br /&gt;
 //&lt;br /&gt;
 integer charToUnicodeIdNumber(string c)&lt;br /&gt;
 {&lt;br /&gt;
     integer cInt = llBase64ToInteger(llStringToBase64(c));&lt;br /&gt;
 &lt;br /&gt;
     if (!(cInt &amp;amp; 0x80000000)) {&lt;br /&gt;
         // UTF-8 single-byte form&lt;br /&gt;
         cInt = cInt &amp;gt;&amp;gt; 24;&lt;br /&gt;
     } else {&lt;br /&gt;
         if ((cInt &amp;amp; 0xe0000000) == 0xc0000000) {&lt;br /&gt;
             // two-byte UTF-8 form:  110v vvvv  10vv vvvv&lt;br /&gt;
             cInt = ((cInt &amp;amp; 0x1f000000) &amp;gt;&amp;gt; 18) |&lt;br /&gt;
                    ((cInt &amp;amp; 0x003f0000) &amp;gt;&amp;gt; 16);&lt;br /&gt;
         } else {&lt;br /&gt;
             // assume three-byte UTF-8 form:  1110 vvvv  10vv vvvv  10vv vvvv&lt;br /&gt;
             cInt = ((cInt &amp;amp; 0x0f000000) &amp;gt;&amp;gt; 12) |&lt;br /&gt;
                    ((cInt &amp;amp; 0x003f0000) &amp;gt;&amp;gt; 10) |&lt;br /&gt;
                    ((cInt &amp;amp; 0x00003f00) &amp;gt;&amp;gt; 8);&lt;br /&gt;
         } // else ignore the 4-byte UTF-8 form&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return cInt;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // This is a memory-savings technique for use with Mono-compiled LSL scripts.&lt;br /&gt;
 // (It probably works in classic LSO too, but not as efficiently.) This technique&lt;br /&gt;
 // stores 15 bits of information in each 16-bit Unicode character. Use the&lt;br /&gt;
 // encode function below to convert any 15-bit data to a Unicode character, and&lt;br /&gt;
 // use the decode function to convert it back to the original 15-bit data.&lt;br /&gt;
 //&lt;br /&gt;
 // This example maps the data values 0 through 0x7fff to the Unicode&lt;br /&gt;
 // characters U-001000 through U-008fff. Use the matching function&lt;br /&gt;
 // decodeCharTo15Bits() to decode the Unicode character back into the original&lt;br /&gt;
 // 15-bit number.&lt;br /&gt;
 //&lt;br /&gt;
 // The technique used here is very similar to the technique used in the &amp;quot;Base 32768&lt;br /&gt;
 // Script&amp;quot; in http://wiki.secondlife.com/wiki/Key_Compression .&lt;br /&gt;
 &lt;br /&gt;
 // Convert any 15-bit integer into a single Unicode character&lt;br /&gt;
 //&lt;br /&gt;
 string encode15BitsToChar(integer num)&lt;br /&gt;
 {&lt;br /&gt;
     // Check the incoming range&lt;br /&gt;
 &lt;br /&gt;
     if (num &amp;lt; 0 || num &amp;gt;= 0x8000) {&lt;br /&gt;
         // illegal input -- do whatever is appropriate&lt;br /&gt;
         return &amp;quot;�&amp;quot;;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     // Bias the incoming numeric value by 0x1000 to avoid illegal Unicode codes:&lt;br /&gt;
 &lt;br /&gt;
     num += 0x1000;&lt;br /&gt;
 &lt;br /&gt;
     // Construct an escaped hex UTF-8 representation and return&lt;br /&gt;
     // it as a Unicode character&lt;br /&gt;
 &lt;br /&gt;
     return llUnescapeURL(&lt;br /&gt;
                   &amp;quot;%&amp;quot; + hexChar2(0xe0 + (num &amp;gt;&amp;gt; 12)) +&lt;br /&gt;
                   &amp;quot;%&amp;quot; + hexChar2(0x80 + ((num &amp;gt;&amp;gt; 6) &amp;amp; 0x3f)) +&lt;br /&gt;
                   &amp;quot;%&amp;quot; + hexChar2(0x80 + (num &amp;amp; 0x3f)));&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 // This is the inverse of encode15BitsToChar(), supra, q.v.&lt;br /&gt;
 // This expects a single 16-bit Unicode character that was created by&lt;br /&gt;
 // encode15BitsToChar() and returns the 15-bit numeric value used to create it.&lt;br /&gt;
 // The 15-bit return value will always be in the range 0x0000 - 0x7fff.&lt;br /&gt;
 //&lt;br /&gt;
 integer decodeCharTo15Bits(string ch)&lt;br /&gt;
 {&lt;br /&gt;
     string utf8 = llEscapeURL(ch); // convert to escaped hex UTF-8&lt;br /&gt;
 &lt;br /&gt;
     return&lt;br /&gt;
         (((integer)(&amp;quot;0x&amp;quot; + llGetSubString(utf8, 1, 2)) &amp;amp; 0x1f) &amp;lt;&amp;lt; 12) +&lt;br /&gt;
         (((integer)(&amp;quot;0x&amp;quot; + llGetSubString(utf8, 4, 5)) &amp;amp; 0x3f) &amp;lt;&amp;lt; 6) +&lt;br /&gt;
          ((integer)(&amp;quot;0x&amp;quot; + llGetSubString(utf8, 7, 8)) &amp;amp; 0x3f) - 0x1000;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 // Returns a Unicode string that encodes twice as many ASCII characters.&lt;br /&gt;
 // Use the matching function decompressAscii() to expand it back into&lt;br /&gt;
 // the original ASCII.&lt;br /&gt;
 //&lt;br /&gt;
 string compressAscii(string s)&lt;br /&gt;
 {&lt;br /&gt;
     integer len = llStringLength(s);&lt;br /&gt;
 &lt;br /&gt;
     // Append a space if needed to make s an even number of chars&lt;br /&gt;
     if (len % 2) {&lt;br /&gt;
        s += &amp;quot; &amp;quot;;&lt;br /&gt;
        ++len;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     string encodedChars;&lt;br /&gt;
     integer i;&lt;br /&gt;
     for (i = 0; i &amp;lt; len; i += 2) {&lt;br /&gt;
         encodedChars += encode15BitsToChar(&lt;br /&gt;
                 charToUnicodeIdNumber(llGetSubString(s, i, i)) &amp;lt;&amp;lt; 7 |&lt;br /&gt;
                 charToUnicodeIdNumber(llGetSubString(s, i+1, i+1)));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return encodedChars;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // This is the inverse of compressAscii()&lt;br /&gt;
 //&lt;br /&gt;
 string uncompressAscii(string s)&lt;br /&gt;
 {&lt;br /&gt;
     string result;&lt;br /&gt;
 &lt;br /&gt;
     integer len = llStringLength(s);&lt;br /&gt;
     integer i;&lt;br /&gt;
     for (i = 0; i &amp;lt; len; ++i) {&lt;br /&gt;
         integer cInt15 = decodeCharTo15Bits(llGetSubString(s, i, i));&lt;br /&gt;
         result += llUnescapeURL(&amp;quot;%&amp;quot; + hexChar2(cInt15 &amp;gt;&amp;gt; 7) +&lt;br /&gt;
                                 &amp;quot;%&amp;quot; + hexChar2(cInt15 &amp;amp; 0x7f));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return result;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 integer lineNumber;&lt;br /&gt;
 string notecardName = &amp;quot;ASCII text&amp;quot;;&lt;br /&gt;
 integer linesRead;&lt;br /&gt;
 string bigText;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         llGetNotecardLine(notecardName, lineNumber++);&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     dataserver(key id, string data)&lt;br /&gt;
     {&lt;br /&gt;
         if (data != EOF) {&lt;br /&gt;
             string compressedLine = compressAscii(data);&lt;br /&gt;
             bigText += compressedLine;&lt;br /&gt;
             llOwnerSay((string)(++linesRead) + &amp;quot;:&amp;quot; + uncompressAscii(compressedLine));&lt;br /&gt;
             llGetNotecardLine(notecardName, lineNumber++);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Also see===&lt;br /&gt;
&lt;br /&gt;
For more ideas about compressing data and other memory-savings techniques, see&lt;br /&gt;
* [[User:Becky_Pippen/Numeric_Storage| Scripting techniques for storing arbitrary binary data without lists]]&lt;br /&gt;
* [[User:Becky_Pippen/Script_Memory_Limits| Memory limits FAQ and Checklist for memory reduction]]&lt;br /&gt;
* [[User:Becky_Pippen/Hashing| Hashing for memory reduction]]&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Hashing&amp;diff=701373</id>
		<title>User:Becky Pippen/Hashing</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Hashing&amp;diff=701373"/>
		<updated>2010-01-11T18:37:08Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: typos and formatting&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Hashing to conserve memory===&lt;br /&gt;
&lt;br /&gt;
Hashing takes a longer item such as an avatar name or [[UUID]] and maps it to something smaller, like an integer or a shorter string. It&#039;s compact, but the trade-off is that different inputs can possibly map to the same hash value by accident.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an illustration of how much memory we can save by hashing UUIDs in a Mono script — from 72 bytes in their native form to just a few bytes each. This illustrates techniques discussed in the rest of this article:&lt;br /&gt;
&lt;br /&gt;
[[Image:Key hash-1.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
There are two problems to solve to make hashing work in LSL: (1) designing a hash function that makes hash codes of the right size, evenly distributed across the hash space, and (2) storing the hash codes efficiently without suffering the overhead of lists. Those two aspects are treated separately below.&lt;br /&gt;
&lt;br /&gt;
===Designing the hash function===&lt;br /&gt;
&lt;br /&gt;
A hash size too small increases the chance of a hash collision. That&#039;s when multiple values of the real data map to the same hash value. A hash size too large wastes memory.&lt;br /&gt;
&lt;br /&gt;
Calculating the probability of hash collisions is similar to calculating the [http://betterexplained.com/articles/understanding-the-birthday-paradox/ &amp;quot;Birthday Paradox&amp;quot; probabilities]. The probability that there will be a duplicate among N random hashes of W bits each is approximately:&lt;br /&gt;
&lt;br /&gt;
 P = 1.0 - e ^ -(N^2 / (2 * 2^W))&lt;br /&gt;
&lt;br /&gt;
The table below shows some examples of the probability &#039;&#039;P&#039;&#039; of having at least one duplicate among &#039;&#039;N&#039;&#039; hashes of various sizes. For example, there is a probability of 0.0001 that there will be a duplicate in a set of 1000 32-bit hash codes.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|15-bit&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|16-bit&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|30-bit&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|32-bit&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|45-bit&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | &#039;&#039;N&#039;&#039;&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | &#039;&#039;P&#039;&#039;&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | &#039;&#039;P&#039;&#039;&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | &#039;&#039;P&#039;&#039;&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | &#039;&#039;P&#039;&#039;&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | &#039;&#039;P&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 100&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.14&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.07&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.000005&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.000001&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.0000000001&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 200&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.46&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.26&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.00002&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.000005&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.0000000006&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 500&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.978&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.85&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.0001&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.00003&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.0000000004&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 1000&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.9995&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.0005&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.0001&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.00000001&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 2000&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.001&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.0005&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.00000006&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 5000&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.01&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.003&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.0000007&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 10000&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.05&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.01&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.000001&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 25000&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.26&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.07&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.000009&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 50000&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.69&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.25&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.00004&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====Hashing UUIDs====&lt;br /&gt;
&lt;br /&gt;
It appears that most [[UUID]]s in SL are constructed with random bits (or sufficiently so for our purposes) in the first six bytes (first 12 hex digits) of the UUID. This lets us make a trivial hash function by extracting some of the hex digits from the UUID. For example, this code extracts the first eight hex digits as a hash:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; string hash(key k)&lt;br /&gt;
 {&lt;br /&gt;
     return llGetSubString((string)k, 0, 7); // first 8 hex chars&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt; &lt;br /&gt;
&lt;br /&gt;
That gives us a hash code with 32 bits of information content (eight hex characters at four bits each).&lt;br /&gt;
&lt;br /&gt;
At this point you might wonder why not reduce the hash code to a regular 32-bit integer that only uses four bytes of memory. Unfortunately,  we can&#039;t do anything with integers except store them in a list, and that makes any string-based solution more efficient, as described below.&lt;br /&gt;
&lt;br /&gt;
If you have any concern that the first few bytes of the UUID are not really as random as you would like, you can make an MD5 hash out of the UUID, then extract some hex digits from the MD5 hash string to make a shorter hash. For example, this hash function extracts eight hex digits from the MD5 hash of the UUID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; string hash(key k)&lt;br /&gt;
 {&lt;br /&gt;
     return llGetSubString(llMD5String((string)k, salt), 0, 7);&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Any salt value works in the code above, as long as it&#039;s consistent. Choose an unguessable value for salt if you want your hashing function to yield different values than other scripts using the same code snippet above. Think of the salt value as serving a purpose similar to the seed value used in pseudo-random number generators.&lt;br /&gt;
&lt;br /&gt;
We can make the hash code even smaller by treating the eight hex digits as a 32-bit numeric value and converting that number into a six-character string using [http://en.wikipedia.org/wiki/Base64 base64] representation. Something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; string hash(key k)&lt;br /&gt;
 {&lt;br /&gt;
     return llGetSubString(llIntegerToBase64(&lt;br /&gt;
         (integer)(&amp;quot;0x&amp;quot; + llGetSubString((string)k, 0, 7))), 0, 5);&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In other words, the same information contained in eight hex characters fits into six base64 characters.&lt;br /&gt;
&lt;br /&gt;
====Hashing avatar names and other strings====&lt;br /&gt;
&lt;br /&gt;
Almost anything can be hashed using [[llMD5String]](). That&#039;s a function historically used for encryption and security, and it&#039;s useful for our hashing needs because it nicely distributes all possible input values across its output range. The MD5 conversion gives us a long string of hex characters, and we can simply extract however many we want to use for a hash. The longer the hash, the less likely there will be a hash collision but the more memory it will consume. This example extracts twelve hex characters from the MD5 result to form a hash code worth 48 bits of information:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; string hash(string input)&lt;br /&gt;
 {&lt;br /&gt;
     string md5 = llMD5String(input, salt);&lt;br /&gt;
     return llGetSubString(md5, 0, 11);&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The problem with this hash function is that the average avatar name in SL is about 14 characters, so a twelve-character hash code is not a dramatic improvement. And then if we store these hash codes in a list, we eat up additional memory with list overhead. To save memory, we need to think of these hash codes as containing so many bits of information, not as characters. Then we need to think of a way to store those bits of information in a more compact form, i.e., without using lists. The next section describes a way of encoding information into Unicode character strings.&lt;br /&gt;
&lt;br /&gt;
===Avoiding list overhead===&lt;br /&gt;
&lt;br /&gt;
It&#039;s very difficult to store data compactly in Mono scripts — there are no arrays; lists incur 20+ bytes of overhead per list element; and string data uses a minimum of two bytes per character. About the only way we have to encode arbitrary data in memory is by encoding the information as Unicode characters.&lt;br /&gt;
&lt;br /&gt;
Using the functions in [[User:Becky_Pippen/Numeric_Storage| this article]], you can store 15 bits of arbitrary data in each 16-bit Unicode character. It&#039;s not quite as straightforward as interpreting the 15 bits as the Unicode character ID because certain Unicode character codes may not be used. For example, we can&#039;t simply use the Unicode character ID 0x000000 to store a numeric value of zero, because 0x000000 is an illegal Unicode character code. So we have to remap any 15-bit number we want to store into some range of legal Unicode characters. The functions [[User:Becky_Pippen/Numeric_Storage#Source_code_example|encode15BitsToChar()]] and [[User:Becky_Pippen/Numeric_Storage#Source_code_example|decodeCharTo15Bits()]] do this remapping by adding 0x1000 to the 15-bit number to make a Unicode character ID in the range 0x1000 through 0x8fff. The result is that a 15-bit hash code can be stored as one character; a 30-bit hash code in two characters, and a 45-bit hash code in three Unicode characters.&lt;br /&gt;
&lt;br /&gt;
===Searching for hash codes when concatenated in a string===&lt;br /&gt;
&lt;br /&gt;
If the consequence of a false positive match is an acceptable risk, then searching a long string for a given hash code can be simplified to a call to [[llSubStringIndex]](). In this example, hash codes are 45-bit values encoded as three Unicode characters each. All hash codes are concatenated into one long string named hashCodes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Returns logical index of the given hash code&lt;br /&gt;
 // or -1 if not found&lt;br /&gt;
 &lt;br /&gt;
 string hashCodes;  // all hash codes are concatenated here&lt;br /&gt;
 &lt;br /&gt;
 integer findHash(string hashCode)&lt;br /&gt;
 {&lt;br /&gt;
     integer index = llSubStringIndex(hashCodes, hashCode);&lt;br /&gt;
     if (index != -1) {&lt;br /&gt;
         index /= 3;  // because each hash code is three Unicode chars&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return index;&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code above will find a false match if the hashCode value accidentally randomly matches characters that span two different hash codes in the hashCodes string. Your script can detect false positives by checking if the match occurred on a boundary of hashSize bytes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Returns logical index of the given hash code&lt;br /&gt;
 // or -1 if not found&lt;br /&gt;
 &lt;br /&gt;
 string hashCodes;  // all hash codes are concatenated here&lt;br /&gt;
 integer hashSize = 6; // number of chars in each hash code&lt;br /&gt;
 &lt;br /&gt;
 integer findHash(string hashCode)&lt;br /&gt;
 {&lt;br /&gt;
     integer index = llSubStringIndex(hashCodes, hashCode);&lt;br /&gt;
     if (index != -1 &amp;amp;&amp;amp; (index % hashSize)) {&lt;br /&gt;
         // this is a false match.&lt;br /&gt;
         // fall back to a loop search, or whatever is appropriate&lt;br /&gt;
     } else if (index != -1) {&lt;br /&gt;
         // a valid match&lt;br /&gt;
         index /= hashSize;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return index;&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code Example===&lt;br /&gt;
&lt;br /&gt;
Here is a complete example of a script that stores avatar names read from a notecard. For each name read, the script searches its memory to see if that avatar name has been encountered before. If not, it adds the name to the list. First let&#039;s get a benchmark of the memory consumption using the naive approach of storing the names as strings in a global list. The notecard I used for a test contains 2000 actual SL avatar names, one per line. There are only 1500 unique names in the notecard, so 500 of them are duplicates scattered throughout.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; integer lineNumber;&lt;br /&gt;
 string notecardName = &amp;quot;names&amp;quot;;&lt;br /&gt;
 integer namesSaved;&lt;br /&gt;
 list hashedNames;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         llGetNotecardLine(notecardName, lineNumber++);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     dataserver(key id, string avName)&lt;br /&gt;
     {&lt;br /&gt;
         if (avName != EOF) {&lt;br /&gt;
             if (llListFindList(hashedNames, [avName]) == -1) {&lt;br /&gt;
                 hashedNames += [avName];&lt;br /&gt;
                 llOwnerSay((string)(++namesSaved) + &amp;quot;: &amp;quot; + avName + &amp;quot;, &amp;quot; +&lt;br /&gt;
                         (string)llGetFreeMemory() + &amp;quot; free&amp;quot;);&lt;br /&gt;
             } else {&lt;br /&gt;
                 llOwnerSay(&amp;quot;duplicate: &amp;quot; + avName);&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             llGetNotecardLine(notecardName, lineNumber++);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The data ate up 56000 bytes of available memory and ran out of memory after saving 1137 unique names. That works out to about 49 bytes consumed per item. That&#039;s very close to what we could have predicted in Mono, based on the data in [[LSL_Script_Memory|this page]] — an average of 14 characters per avatar name uses 28 bytes of memory in Mono, plus 22 bytes of overhead per list element for a total of 28+22=50 bytes each.&lt;br /&gt;
&lt;br /&gt;
Here is the same script but with the functions needed to save the avatar identities as 45-bit hash codes encoded as three Unicode characters each. It stores 1500 hashed names in just 9000 bytes. Even adding the 2500 extra bytes of program code to support hashing, that comes out to a total 11500 bytes consumed to store 1500 avatar identities, or 7.7 bytes each, compared to 49 bytes each on the average for the naive list approach above. &#039;&#039;&#039;That&#039;s an 84% net reduction in memory consumption.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Demo - store avatar identities as 45-bit hashes encoded in 3 Unicode&lt;br /&gt;
 // chars each, concatenated into one string.&lt;br /&gt;
 // By Becky Pippen, 2010, contributed to the public domain.&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 // Converts n = [0..0xff] to &amp;quot;%&amp;quot; + two hex characters&lt;br /&gt;
 //&lt;br /&gt;
 string escapeHexChar(integer n)&lt;br /&gt;
 {&lt;br /&gt;
     string hexChars = &amp;quot;0123456789abcdef&amp;quot;;&lt;br /&gt;
     return &amp;quot;%&amp;quot; +&lt;br /&gt;
           llGetSubString(hexChars, n &amp;gt;&amp;gt; 4, n &amp;gt;&amp;gt; 4) +&lt;br /&gt;
           llGetSubString(hexChars, n &amp;amp; 0xf, n &amp;amp; 0xf);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 // Encode 15 bits of data as a single Unicode character. Use decodeCharTo15Bits()&lt;br /&gt;
 // to recover the 15-bit data.&lt;br /&gt;
 //&lt;br /&gt;
 // The technique used here is very similar to the technique used in the &amp;quot;Base 32768&lt;br /&gt;
 // Script&amp;quot; in http://wiki.secondlife.com/wiki/Key_Compression .&lt;br /&gt;
 &lt;br /&gt;
 string encode15BitsToChar(integer num)&lt;br /&gt;
 {&lt;br /&gt;
     // Use only the lower 15 bits and bias by 0x1000 to avoid illegal Unicode IDs:&lt;br /&gt;
 &lt;br /&gt;
     num = 0x1000 + (num &amp;amp; 0x00007fff);&lt;br /&gt;
 &lt;br /&gt;
     // Construct an escaped hex UTF-8 representation and&lt;br /&gt;
     // return it as one Unicode character:&lt;br /&gt;
 &lt;br /&gt;
     return llUnescapeURL(&lt;br /&gt;
               escapeHexChar(0xe0 + (num &amp;gt;&amp;gt; 12)) +&lt;br /&gt;
               escapeHexChar(0x80 + ((num &amp;gt;&amp;gt; 6) &amp;amp; 0x3f)) +&lt;br /&gt;
               escapeHexChar(0x80 + (num &amp;amp; 0x3f)));&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 //****************************************&lt;br /&gt;
 // demo -- store avatar identities as compressed 45-bit hashes of their name&lt;br /&gt;
 &lt;br /&gt;
 integer lineNumber;       // for the notecard reader&lt;br /&gt;
 string notecardName = &amp;quot;names&amp;quot;; // notecard of av names, one per line&lt;br /&gt;
 integer namesSaved;       // count how many successfully saved&lt;br /&gt;
 string hashedNames;       // all the hashes get concatenated in this string&lt;br /&gt;
 integer hashSize = 3;     // num of Unicode chars per encoded hash code&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         llGetNotecardLine(notecardName, lineNumber++);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     dataserver(key id, string avName)&lt;br /&gt;
     {&lt;br /&gt;
         if (avName != EOF) {&lt;br /&gt;
             // Pick off 48 bits of information to start with:&lt;br /&gt;
             string md5 = llMD5String(avName, 0);&lt;br /&gt;
             integer n1 = (integer)(&amp;quot;0x&amp;quot; + llGetSubString(md5, 0, 7));&lt;br /&gt;
             integer n2 = (integer)(&amp;quot;0x&amp;quot; + llGetSubString(md5, 8, 11));&lt;br /&gt;
 &lt;br /&gt;
             // Map 45 of the bits into three characters. We cannot encode Unicode&lt;br /&gt;
             // directly from Unicode IDs, but we can do it by constructing an escaped&lt;br /&gt;
             // hex UTF-8 string representation and converting that to Unicode&lt;br /&gt;
             //&lt;br /&gt;
             // |------------------ n1 ------------------|  |------- n2 -------|&lt;br /&gt;
             // x000 0000  0000 0000  x000 0000  0000 0000  x000 0000  0000 0000&lt;br /&gt;
             //  |------char1------|   |------char2------|   |------char3------|&lt;br /&gt;
 &lt;br /&gt;
             string encoded3Chars = &lt;br /&gt;
                 encode15BitsToChar(n1 &amp;gt;&amp;gt; 16) +&lt;br /&gt;
                 encode15BitsToChar(n1) +&lt;br /&gt;
                 encode15BitsToChar(n2);&lt;br /&gt;
 &lt;br /&gt;
             integer idx = llSubStringIndex(hashedNames, encoded3Chars);&lt;br /&gt;
             if (idx == -1) {&lt;br /&gt;
                 // this is a new one, so save it&lt;br /&gt;
                 hashedNames += encoded3Chars;&lt;br /&gt;
                 llOwnerSay((string)(++namesSaved) + &amp;quot;: &amp;quot; + avName + &amp;quot;, &amp;quot; +&lt;br /&gt;
                            (string)llGetFreeMemory() + &amp;quot; free&amp;quot;);&lt;br /&gt;
             } else if (idx % hashSize) {&lt;br /&gt;
                 llOwnerSay(&amp;quot;WARNING!!! False match for &amp;quot; + avName);&lt;br /&gt;
             } else {&lt;br /&gt;
                 llOwnerSay(&amp;quot;Duplicate, already saved: &amp;quot; + avName);&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             llGetNotecardLine(notecardName, lineNumber++);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Also see===&lt;br /&gt;
&lt;br /&gt;
For more ideas about compressing bits into Unicode strings, see&lt;br /&gt;
* [[User:Becky_Pippen/Numeric_Storage| Scripting techniques for storing arbitrary binary data without lists]]&lt;br /&gt;
* [[User:Becky_Pippen/Script_Memory_Limits| Memory limits FAQ and Checklist for memory reduction]]&lt;br /&gt;
&lt;br /&gt;
If you need to store many keys without losing any bits, see the suggestions at [[Key_Compression]].&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Measure_Script_Memory_Usage&amp;diff=700393</id>
		<title>User:Becky Pippen/Measure Script Memory Usage</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Measure_Script_Memory_Usage&amp;diff=700393"/>
		<updated>2010-01-10T06:21:53Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: additional info&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Measuring Script Memory Usage ===&lt;br /&gt;
In addition to upcoming new tools that will let us conveniently measure scripts in use,&lt;br /&gt;
scripters can also use [[llGetFreeMemory]]() to make more detailed measurements of how specific functions or&lt;br /&gt;
statements affect memory usage.&lt;br /&gt;
The memory a script uses is:&lt;br /&gt;
* Starting memory (65536 bytes for [[Mono]], 16384 for [[LSO|LSL]])&lt;br /&gt;
* minus program statements   (e.g., a = 2;)&lt;br /&gt;
* minus static (pre-defined) variables  (e.g., integer a;)&lt;br /&gt;
* minus dynamic (created when running) data and overhead   (e.g., f(&amp;quot;a&amp;quot; + &amp;quot;b&amp;quot;) )&lt;br /&gt;
Here is an LSL statement that reports how much free memory you have at the moment it&#039;s called:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llOwnerSay((string)llGetFreeMemory() + &amp;quot; bytes free&amp;quot;);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To report how much memory is used (instead of free) in a Mono script, subtract&lt;br /&gt;
free from total memory:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;llOwnerSay((string)(65536 - llGetFreeMemory()) + &amp;quot; bytes used&amp;quot;);&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For LSL-compiled scripts, change 65536 to 16384.&lt;br /&gt;
&lt;br /&gt;
It makes a difference where you place [[llGetFreeMemory]]() in your script. To see how&lt;br /&gt;
much memory is used by the script&#039;s program code and static (pre-defined) variables,&lt;br /&gt;
put [[llGetFreeMemory]]() as the first line in [[state_entry]]() or [[on_rez]]() in the default&lt;br /&gt;
state. To see how much memory your script used including all runtime dynamic data,&lt;br /&gt;
call [[llGetFreeMemory]]() as the last statement executed after your script has created&lt;br /&gt;
all the data it&#039;s going to.&lt;br /&gt;
&lt;br /&gt;
To see how much memory is used by a one or a few program statements,&lt;br /&gt;
measure the memory before and after and take the difference. Be aware that your&lt;br /&gt;
instrumentation for measuring memory might itself consume some memory&lt;br /&gt;
if it uses variables or function calls, so do all that prior to the measurement.&lt;br /&gt;
&lt;br /&gt;
Also be aware that the way the LSL and Mono back ends allocate memory (and&lt;br /&gt;
perhaps from fluctuations due to garbage collection), you might&lt;br /&gt;
get varying results from individual measurements, so it&#039;s sometimes useful to&lt;br /&gt;
take an average.&lt;br /&gt;
&lt;br /&gt;
For example, suppose we want to measure how much memory gets consumed each time&lt;br /&gt;
your script saves a short string to a global list. Here is the statement that we want to&lt;br /&gt;
measure:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;aList += &amp;quot;hello&amp;quot;;  // appends one 5-char string to the list&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To measure the memory used by this statement, we&#039;ll average the memory consumption over a hundred loops.&lt;br /&gt;
We&#039;ll also globally declare the temporary variables we&#039;ll need for the measurement&lt;br /&gt;
so that between the start and end of the test, there will be no extra variables allocated&lt;br /&gt;
or function calls made except the code being measured:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
 list aList;&lt;br /&gt;
 integer startFreeMem;&lt;br /&gt;
 integer endFreeMem;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
        integer loops = 100;&lt;br /&gt;
        startFreeMem = llGetFreeMemory();&lt;br /&gt;
 &lt;br /&gt;
        while (--loops &amp;gt;= 0) {&lt;br /&gt;
            aList += &amp;quot;hello&amp;quot;;  // appends one 5-char string to the list&lt;br /&gt;
        }&lt;br /&gt;
 &lt;br /&gt;
        endFreeMem = llGetFreeMemory();&lt;br /&gt;
        llOwnerSay(&amp;quot;Change in free mem: &amp;quot; + (string)(startFreeMem - endFreeMem));&lt;br /&gt;
     }&lt;br /&gt;
 }&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
By the way, the results I got from this test shows that it takes 3486 bytes of memory to&lt;br /&gt;
append 100 copies of &amp;quot;hello&amp;quot; to a global list when compiled with LSL, and 752&lt;br /&gt;
bytes when compiled with Mono. These results are a little different than those given in&lt;br /&gt;
[[LSL_Script_Memory|this page]], and that&#039;s why it can&lt;br /&gt;
be illuminating to make these measurements in your own scripts. (In this example, the&lt;br /&gt;
[[LSL_Hacks|LSL assignment hack]] would help minimize memory consumption for the LSO case.)&lt;br /&gt;
===Caveats and geekystuffs===&lt;br /&gt;
There are several reasons why [[llGetFreeMemory]]() can give unexpected results:&lt;br /&gt;
* &#039;&#039;&#039;In Mono, memory may be allocated in blocks&#039;&#039;&#039; -- For example, a small change in the script might cause the free memory to change by 512 bytes. To get a better idea of the average memory used by a particular script construct, repeat it a number of times in the script and divide by the number of instances. For more information, see {{Jira|SVC-4387}}.&lt;br /&gt;
* &#039;&#039;&#039;[[llGetFreeMemory]]() reports the low-water mark before garbage collection&#039;&#039;&#039; -- For example, if you fill a list or string with thousands of bytes of data and then clear the list or string, [[llGetFreeMemory]]() will continue to report the lowest amount of memory that occurred and does not reflect the fact that a bunch of memory might be available if only the garbage collector would collect it. To reduce lag, the garbage collector typically frees up memory only when the script runs out of memory and more memory is needed. This is also illustrated in the code example at the end of this article. For more information, see the comments at:&lt;br /&gt;
** [[llGetFreeMemory]]()&lt;br /&gt;
** {{Jira|SVC-4387}}&lt;br /&gt;
** {{Jira|SVC-1852}}&lt;br /&gt;
* &#039;&#039;&#039;Memory fragmentation&#039;&#039;&#039; -- The order in which memory is allocated and freed can cause [[llGetFreeMemory]]() to act differently. In LSO, it might be reporting the largest single block of free memory instead of the total free bytes. It&#039;s not clear how fragmentation might affect [[llGetFreeMemory]]() in Mono. For more information, see the comments in:&lt;br /&gt;
** {{Jira|SVC-116}}&lt;br /&gt;
** {{Jira|SVC-168}}&lt;br /&gt;
* &#039;&#039;&#039;First run different than a reset&#039;&#039;&#039; -- In Mono, the amount of free memory reported by [[llGetFreeMemory]]() as the first line in [[state_entry]]() to be different on the first run after compiling compared to subsequent runs after a script reset.&lt;br /&gt;
* &#039;&#039;&#039;Mischievous spirits in Mono&#039;&#039;&#039; -- Even Mono experts describe Mono&#039;s memory management and garbage collection as squirrely and non-deterministic at best, so expect a few puzzling results in Mono.&lt;br /&gt;
&lt;br /&gt;
===Code example===&lt;br /&gt;
If you find yourself making lots of measurements and comparisons, you might find a test scaffolding useful. Here is an example of some things you can do. To run your own tests, replace the lines below in touch_start() with your own tests, using the existing code as examples. Insert a call to mem() in between each test. The results in the chat log will be easy to read, and will also automatically log if the tests were run on Mono or LSO so you won&#039;t have to remember later.&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Demo of a memory-usage reporter &lt;br /&gt;
 //&lt;br /&gt;
 // To use:  call initMemReporter() in the start of the script, then&lt;br /&gt;
 // call mem() at any time to report memory used and amount of change.&lt;br /&gt;
 // This adjusts the numbers automatically depending on whether the&lt;br /&gt;
 // script is compiled for Mono or classic LSO.&lt;br /&gt;
 //&lt;br /&gt;
 integer isMono;        // set at runtime to TRUE if Mono VM, FALSE if LSO&lt;br /&gt;
 integer totalMem;      // set at runtime to 65536 or 16384&lt;br /&gt;
 string  vm;            // set at runtime to &amp;quot;Mono&amp;quot; or &amp;quot;LSO&amp;quot;&lt;br /&gt;
 integer lastUsedMem;&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 // Call this once at the start of the script, then call mem()&lt;br /&gt;
 // at any time to report memory usage. This function caches a&lt;br /&gt;
 // few variables so that they don&#039;t have to be recomputed in&lt;br /&gt;
 // mem() every time.&lt;br /&gt;
 //&lt;br /&gt;
 initMemReporter()&lt;br /&gt;
 {&lt;br /&gt;
     // Test if we&#039;re running on LSO or Mono VM using&lt;br /&gt;
     // the difference between the two reported in&lt;br /&gt;
     // https://jira.secondlife.com/browse/SVC-3760&lt;br /&gt;
 &lt;br /&gt;
     isMono = llToLower(&amp;quot;Ü&amp;quot;) != &amp;quot;Ü&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
     // Establish total memory:&lt;br /&gt;
 &lt;br /&gt;
     if (isMono) {&lt;br /&gt;
         totalMem = 65536;&lt;br /&gt;
         vm = &amp;quot;(Mono)&amp;quot;;&lt;br /&gt;
     } else {&lt;br /&gt;
         totalMem = 16384;&lt;br /&gt;
         vm = &amp;quot;(LSO)&amp;quot;;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     lastUsedMem = totalMem - llGetFreeMemory();&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Report the memory used and the change from the last report&lt;br /&gt;
 //&lt;br /&gt;
 mem(string label)&lt;br /&gt;
 {&lt;br /&gt;
     integer freeMem = llGetFreeMemory();&lt;br /&gt;
     integer usedMem = totalMem - freeMem;&lt;br /&gt;
 &lt;br /&gt;
     llOwnerSay(&lt;br /&gt;
         vm + &lt;br /&gt;
         &amp;quot; used=&amp;quot; + (string)usedMem +&lt;br /&gt;
         &amp;quot;, free=&amp;quot; + (string)freeMem +&lt;br /&gt;
         &amp;quot;, change=&amp;quot; + (string)(lastUsedMem - usedMem) +&lt;br /&gt;
         &amp;quot; \t&amp;quot; + label);&lt;br /&gt;
 &lt;br /&gt;
     lastUsedMem = usedMem;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     state_entry()&lt;br /&gt;
     {&lt;br /&gt;
         initMemReporter();&lt;br /&gt;
         mem(&amp;quot;initial conditions&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         integer a;&lt;br /&gt;
         float f;&lt;br /&gt;
         list intList;&lt;br /&gt;
         intList = [];&lt;br /&gt;
         integer LOOPS = 1000;&lt;br /&gt;
         integer i = LOOPS;&lt;br /&gt;
         mem(&amp;quot;Start of tests&amp;quot;);&lt;br /&gt;
         &lt;br /&gt;
         //************* Test 1&lt;br /&gt;
         intList += [0];&lt;br /&gt;
         mem(&amp;quot;after adding the first element to a list&amp;quot;);&lt;br /&gt;
         &lt;br /&gt;
         //************* Test 2&lt;br /&gt;
         while (--i &amp;gt;= 0) {&lt;br /&gt;
             intList += 0;&lt;br /&gt;
         }&lt;br /&gt;
         mem(&amp;quot;after filling a list with &amp;quot; + (string)LOOPS + &amp;quot; zeros&amp;quot;);&lt;br /&gt;
         &lt;br /&gt;
         //************* Test 3&lt;br /&gt;
         intList = [];&lt;br /&gt;
         mem(&amp;quot;after clearing the previous list&amp;quot;);&lt;br /&gt;
         &lt;br /&gt;
         //************* Test 4&lt;br /&gt;
         for (i = LOOPS; --i &amp;gt;= 0; ) {&lt;br /&gt;
             intList += 0;&lt;br /&gt;
         }&lt;br /&gt;
         mem(&amp;quot;after filling the list again with &amp;quot; + (string)LOOPS + &amp;quot; zeros&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
The results in Mono are:&lt;br /&gt;
&amp;lt;lsl&amp;gt; (Mono) used=6552, free=58984, change=-18     initial conditions&lt;br /&gt;
 (Mono) used=6668, free=58868, change=-116     Start of tests&lt;br /&gt;
 (Mono) used=6668, free=58868, change=0     after adding the first element to a list&lt;br /&gt;
 (Mono) used=25938, free=39598, change=-19270     after filling a list with 1000 zeros&lt;br /&gt;
 (Mono) used=26660, free=38876, change=-722     after clearing the previous list&lt;br /&gt;
 (Mono) used=26660, free=38876, change=0     after filling the list again with 1000 zeros &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Also see===&lt;br /&gt;
For ways to reduce memory usage in scripts, see [[User:Becky_Pippen/Script_Memory_Limits| this checklist of things you can do]].&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen&amp;diff=698832</id>
		<title>User:Becky Pippen</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen&amp;diff=698832"/>
		<updated>2010-01-06T22:36:58Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: minor formatting&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== About ==&lt;br /&gt;
I love helping new residents get addicted... er, I mean acclimated to Second Life. Here are some notes I&#039;ve picked up along the way that might be of help.&lt;br /&gt;
== SL Glossary ==&lt;br /&gt;
[http://www.inkwelle.com/glossary_nci.shtml Second Life&#039;s most complete glossary]&lt;br /&gt;
== What is Mono? ==&lt;br /&gt;
=== John and Mary ===&lt;br /&gt;
John and Mary are LSL scripters. John prefers the old-fashioned way of doing things, while Mary enjoys all the latest new technology, like Mono. They both write LSL scripts. John runs his scripts under the old way, and Mary runs hers using Mono.&lt;br /&gt;
&lt;br /&gt;
When John writes an LSL script and clicks the Save button, the client viewer compiles the script into proprietary bytecode and uploads it to the servers. The server runs the script by using a proprietary interpreter to interpret the bytecode. It runs slow.&lt;br /&gt;
&lt;br /&gt;
Mary writes the identical script -- no change in the LSL language syntax. When she presses &amp;quot;Save&amp;quot;, the client uploads her script text to the servers where it gets compiled into standardized CIL assembly language. (CIL is a bytecode that originally came from Microsoft&#039;s .NET technology.) Because the Linden servers are Linux machines, there&#039;s no .NET framework  available to run the CIL code. So the servers use the open-source Mono framework to execute the CIL. Mono includes an open-source JIT (&amp;quot;Just in Time&amp;quot;) compiler that converts the CIL bytecode into machine code as it&#039;s needed, and that compiled machine code is what makes the script execute faster than the old way.&lt;br /&gt;
=== How it all Flows ===&lt;br /&gt;
Old way:&lt;br /&gt;
 LSL  ---&amp;gt; LSL compiler ---&amp;gt; proprietary bytecode ---&amp;gt; LSL bytecode interpreter&lt;br /&gt;
&lt;br /&gt;
New way:&lt;br /&gt;
 LSL                        ---&amp;gt; LSL compiler   | &lt;br /&gt;
 C# (future)                ---&amp;gt; C# compiler    |  ---&amp;gt; CIL bytecode ---&amp;gt; Mono CIL execution engine&lt;br /&gt;
 Other languages (future)   ---&amp;gt; other compiler |&lt;br /&gt;
=== Do Scripts Running in Mono Get More Memory? ===&lt;br /&gt;
Yes, it&#039;s true that each Mono script gets 64K of memory to use. Non-Mono scripts have to fit inside 16K of memory. (That&#039;s compiled code plus stack plus heap.) Unfortunately, Mono-compiled code is a bit of a memory hog and can take up to 4 times as much memory as the old runtime environment. So, scripts using Mono end up with a little more memory to play with depending on the code-to-data ratio, but not four times as much. The advantage to the servers is that small Mono scripts only consume as much server memory as they actually use, while the older LSL virtual machine had to allocate a chunk of 16K of memory to every script regardless of how little memory it actually needed. &lt;br /&gt;
=== Evolving Terminology ===&lt;br /&gt;
LSL - Used to refer to the whole system -- the language, the front-end compiler, and the back-end interpreter. Now it&#039;s confusing because it&#039;s used in one of two ways and its meaning depends on the context:  (1) LSL is the language, the source code, which has not changed with Mono. E.g., &amp;quot;Take this LSL script and compile it with Mono and it will run faster.&amp;quot; Or (2) LSL refers to the old compiler front-end and interpreter virtual machine back-end. E.g., &amp;quot;When I compile this script with LSL it runs very slowly.&amp;quot; Both meanings are used in the sentence, &amp;quot;This LSL script was compiled with LSL, but that LSL script was compiled with Mono.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The term LSO refers to the bytecode generated by the traditional LSL compiler and executed by the old LSL runtime, and is a term that can be used to distinguish the old LSL tool set from the Mono tool set.&lt;br /&gt;
=== Can We Use Other Languages? ===&lt;br /&gt;
Mono just runs CIL bytecode. It doesn&#039;t know or care what the original language was. At this time (mid-2008) only LSL source code can be compiled into CIL and run on Mono. But maybe someday we will be able to compile C# and possibly other languages into CIL which will run under Mono. A single object could contain scripts that originally were written in different languages. &lt;br /&gt;
&lt;br /&gt;
The LSL language -- the source syntax -- will probably be supported for a very long time. &lt;br /&gt;
&lt;br /&gt;
However, the servers will support both back-ends for a very long time -- the one that executes the old proprietary LSL bytecode, and the new Mono virtual machine that executes standard CIL. All the millions of existing LSL scripts compiled the old way will continue to run indefinitely. New LSL scripts may be compiled using the old LSL compiler or the new Mono compiler.&lt;br /&gt;
=== What To Expect After Mono Hits the Main Grid ===&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;Old LSL scripts already compiled:&#039;&#039;&#039;&#039;&#039; will continue to run indefinitely. You don&#039;t have to do a thing.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;Any LSL script if you have the source code:&#039;&#039;&#039;&#039;&#039; can be recompiled under the old LSL compiler or the new Mono compiler, at your choice, for a long time to come.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;&#039;&#039;C# and other languages:&#039;&#039;&#039;&#039;&#039; probably added at some future time.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== LSL Timings and Rates ==&lt;br /&gt;
Here are some LSL function times and event rates comparing scripts compiled and run using the old LSL toolset and using Mono.&lt;br /&gt;
&lt;br /&gt;
The basic test framework for these tests is to set i to a large value, and take the difference in llGetTime() before and after the loop shown below (the loop is partially unrolled to make the loop control structure overhead negligible), and take several measurements in multiple Class 5 sims that are running consistently with a total frame time under 10 ms, where each test runs for 30 - 120 minutes, and measured several times during a 24-hour period until the numbers converge with a variance of 10% or less:&lt;br /&gt;
 while (--i &amp;gt;= 0) {&lt;br /&gt;
     f(); f(); f(); f(); f(); f(); f(); f();&lt;br /&gt;
     f(); f(); f(); f(); f(); f(); f(); f();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
In a Mono sim, the result of the first run after script reset is ignored to eliminate the overhead of the VM initialization, JIT compilation, etc.&lt;br /&gt;
&lt;br /&gt;
Note that these are stopwatch times. How rapidly you can call a function or how often an event handler is invoked doesn&#039;t necessarily correspond to how much those functions and events lag a sim because of all the throttling and scheduling going on behind the scenes inside the run-time engine.&lt;br /&gt;
&lt;br /&gt;
=== Functions, operators, &amp;amp; control structures ===&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|call to empty f() { }&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.33&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.0026&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|for loop overhead&lt;br /&gt;
for (i = 0; i == 0; ++i) { }&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.07&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.001&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/loop &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This test measures the overhead of a for loop that iterates an empty block just once, including its setup. The code generated for this should be something like one integer assignment, one increment, and one test-and-branch. Or as one developer said, &amp;quot;if it&#039;s not, it should be.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|state change&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.14&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|22.2&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/transition &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
State changes in Mono are tied to the sim frame rate of 45 fps (per Vektor Linden). The test code for state changes is an outer loop around 16 state changes like this:&lt;br /&gt;
 . . .&lt;br /&gt;
 state state&#039;&#039;N&#039;&#039;   { state_entry() { state state&#039;&#039;N+1&#039;&#039;; } }&lt;br /&gt;
 state state&#039;&#039;N+1&#039;&#039; { state_entry() { state state&#039;&#039;N+2&#039;&#039;; } }&lt;br /&gt;
 . . . etc.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|v = &amp;lt;PI,PI,PI&amp;gt; + &amp;lt;PI,PI,PI&amp;gt; * PI;&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.43&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.025&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/statement &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|call to llCSV2List(llList2CSV(x));&lt;br /&gt;
where x is a list of 10 integers&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|3.2&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|4 - 7&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
So far, the Mono results vary too much for any meaningful result.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|call to llDetectedName()&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|1.1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.8&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|call to llGetInventoryName(INVENTORY_TEXTURE, 0);&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.52&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.21&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|call to llGetPos();&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.44&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.045&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|call to llList2Vector(x, 0);&lt;br /&gt;
where x = [ ZERO_VECTOR ]&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.55&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.014&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|call to llMD5String(s,0);&lt;br /&gt;
where s is a 64-char string&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|1.4&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|1.4&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|call to llMessageLinked(LINK_THIS, 0, &amp;quot;&amp;quot;, NULL_KEY);&lt;br /&gt;
One consumer script with empty link_message() { }&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|1.5&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.9&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|Two consumer scripts&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|1.8&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|1.2&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|Four consumer scripts&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|2.4&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|1.8&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|Eight consumer scripts&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|4.4&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|3.3&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|16 consumer scripts&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|9 - 20&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|7 - 15&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|32 - 64 consumer scripts&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|22.2&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|22.2&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The time it takes to call llMessageLinked() is dependent on the number of consumers of the message, up to about 20-30 consumer scripts. At that point, the time to send a link message becomes constant at one call per 22.2 ms, which is suspiciously similar to the sim&#039;s basic frame rate.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|call to llParseString2List(s,a,b)&lt;br /&gt;
where s is 2000 random digits, a=[&amp;quot;0&amp;quot;,&amp;quot;1&amp;quot;]; b=[&amp;quot;2&amp;quot;,&amp;quot;3&amp;quot;];&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|45&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|24&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|call to llSay(-2, &amp;quot;0123456789&amp;quot;);&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.66&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.3 - 0.8&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The Mono results vary too much for meaningful results.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|call to llSetLinkAlpha();&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|1.0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.52&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|call to llSetText(s,c,a)&lt;br /&gt;
where string s is 20 chars&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.79&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|0.38&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|call to llSubStringIndex(s, &amp;quot;X&amp;quot;); where s is 10000 digits not containing &amp;quot;X&amp;quot;&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|16&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|22&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|ms/call &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Event handlers ===&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|Maximum rate of dataserver() events after llGetNotecardLine()&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|6.4&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|6.4&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|events/sec&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|Maximum rate of link_message() events&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|42-45&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|42-45&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|events/sec&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The results were highly variable over several tens of millions of link_message() events in three lightly loaded sims, but I wonder if the maximum rate is tied to the sim frame rate of 45 fps.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|Maximum rate of listen() events&lt;br /&gt;
1 listener per script, any number of scripts per prim, any number of prims in linkset&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|14.7&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|14.7&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|events/sec&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|Two listeners per script&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|11&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|11&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|events/sec&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|Three listeners per script&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|8.8&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|8.9&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|events/sec&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|Maximum rate of sensor() events after llSensorRepeat()&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|15&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|22&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|events/sec&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The maximum rate of sensor() events seems to be independent of the sensor radius or number of things detected.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;300&amp;quot; rowspan=&amp;quot;2&amp;quot;|Maximum rate of timer() events&lt;br /&gt;
Maximum rate of touch() events&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|LSL&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Mono&lt;br /&gt;
|align=&amp;quot;center&amp;quot; width=&amp;quot;80&amp;quot;|Units&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|22&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|22&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|events/sec&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
About half the sim&#039;s basic frame rate of 45 fps.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Animation Frame Rate ==&lt;br /&gt;
=== The Problem ===&lt;br /&gt;
You may have heard rumors that animations (BVH files) get reduced to 12 FPS when uploaded or played, and there&#039;s no need to make an animation faster than 12 frames per second (FPS). Other rumors say that fast animation frame rates give smoother animations, while other rumors say that you should use the lowest animation frame rate possible. Confused? Here&#039;s some information that might help.&lt;br /&gt;
=== The SL Viewer Interpolates ===&lt;br /&gt;
Part of the confusion is that the term &amp;quot;frame rate&amp;quot; means different things in different contexts. The SL viewer renders frames of the scene, including any animations in the scene, as fast as it can. You can always see how fast the client is rendering frames by looking for the FPS number in the Basic section in the Statistics Bar (Ctrl-Shift-1). The frame rate in an animation file is just there to establish the time scale of the animation information. It&#039;s a different kind of &amp;quot;frame.&amp;quot; So how do the two &amp;quot;frame rates&amp;quot; relate and how does it all work? It&#039;s simple -- when the viewer renders a frame, it determines how far along it is in the animation and interpolates the avatar&#039;s bone rotations based on the nearest two frames in the animation file.&lt;br /&gt;
&lt;br /&gt;
Refer to the following illustration to see how you can demonstrate this interpolation. Using your favorite animation editor, make an animation file consisting of just three or four frames. The first frame is the requisite reference frame, then frame 2 is the first key frame of the animation that defines the start of a movement. Frame 3 is the last frame of the movement. Optionally you can add a frame 4 that is a copy of frame2 to make a loop. Make a large movement between frames 2 and 3, such as arms straight down in frame 2 and straight up in frame 3. Set the animation for 1 frame per second. Upload the short animation file and set the looping parameters if needed so that the animation loops continuously.&lt;br /&gt;
&lt;br /&gt;
With the animation set to one frame per second, the arms should start at the sides, then one second later be raised above the head, then two seconds after the start of the animation, the arms should be back at the sides, then repeat. What happens if the client renders frames faster than one per second? If the viewer didn&#039;t interpolate, you would expect the avatar&#039;s arms to snap suddenly from one position to the other... snap up... snap down... etc. at one-second intervals. But that&#039;s not what happens at all. Regardless of your viewer frame rate, you&#039;ll see your avatar&#039;s hands moving smoothly, up and down, with all the intermediate positions between animation frames interpolated smoothly. To examine the effect in more detail, click Advanced-&amp;gt;Character-&amp;gt;Slow Motion Animations, then you&#039;ll have a whole bunch of interpolated rendered frames to examine.&lt;br /&gt;
&lt;br /&gt;
[[Image:Animation-interpolation.jpg|500px|||Viewer interpolates animation frames]]&lt;br /&gt;
&lt;br /&gt;
=== Asynchronous Frame Rates ===&lt;br /&gt;
In the following timeline example, the top lines represent the frames in an animation file, and the bottom lines show how fast the viewer renders frames and where the rendered frames correspond in time to the animation file. In this illustration, the animation frames are numbered starting at two, because frame one is the reference frame that never gets rendered. When the client is rendering the first frame of the animation (render frame 0 in the illustration), it corresponds to frame 2 in the animation file. In this illustration, the client is running at a fairly constant rate a little more than three times the rate of frames in the animation file:&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
animation frame number        2                         3                         4                         5&lt;br /&gt;
&lt;br /&gt;
animation frames:             K-------------------------K-------------------------K-------------------------K-----&lt;br /&gt;
&lt;br /&gt;
viewer rendered frames:       |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|&lt;br /&gt;
&lt;br /&gt;
viewer frame number:          0       1       2       3       4       5       6       7       8       9      10&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
When the viewer is rendering frame 0, the bone rotations exactly correspond to frame 2 in the animation file. After that, the time when a frame is rendered will almost always fall somewhere between two of the animation frames and the client will interpolate the bone rotations. For example, when the client renders frame 1, the bone rotations will be something between the rotations in frames 2 and 3 of the animation file, but closer to the rotations in frame 2. &lt;br /&gt;
&lt;br /&gt;
If the client is running slow in comparison to the animation file, then some data in the animation file must be skipped:&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
animation frame number        2     3     4     5     6     7     8     9    10    11    12    13    14    15 &lt;br /&gt;
&lt;br /&gt;
animation key frames:         K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K-----K---&lt;br /&gt;
&lt;br /&gt;
viewer rendered frames:       |--------------|---------------|---------------|---------------|---------------|--&lt;br /&gt;
&lt;br /&gt;
viewer frame number:          0              1               2               3               4               5 &lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
In this scenario, when it&#039;s time for the client to render frame 1, the animation information is interpolated from frames 4 and 5 in the animation file, which means that any quick movement defined in frames 3 and 4 of the animation file will have been ignored that time around. If the animation loops, then perhaps on a subsequent loop the frames will align differently and a different set of animation frames will be ignored.&lt;br /&gt;
&lt;br /&gt;
=== Other Considerations ===&lt;br /&gt;
There are additional forces at work you need to be aware of:&lt;br /&gt;
&lt;br /&gt;
1. The higher the animation frame rate, the larger the file that has to be sent from the sim to every client in viewing range of the animation. Smaller is less laggy.&lt;br /&gt;
&lt;br /&gt;
2. When you upload an animation, the viewer silently drops small movements. It does this by comparing each bone&#039;s rotation in two adjacent frames, then dropping any data for bones that don&#039;t move much. The exact algorithm is complicated, but in general the threshold is a few degrees in any axis from one frame to the next. If a bone&#039;s rotation changes from one frame to the next less than 3 or 4 degrees in all axes, it might be dropped. If the movement is more than about 10 degrees in any axis, it&#039;s likely to be retained. If an animation is made with a high frame rate, then the movement from frame to frame will be smaller and more movements will be dropped. If the animation frames are less frequent, then the movement between frames will be larger and more likely to be retained.&lt;br /&gt;
&lt;br /&gt;
3. When the viewer interpolates bone rotations from the animation file, it uses a linear interpolation between the nearest frames, which sometimes looks a bit robotic. A higher animation rate lets you specify movements in finer resolution and make the movement start slow, get faster in the middle of the movement, then slow down when it reaches its maximum extent. &lt;br /&gt;
&lt;br /&gt;
=== Summary ===&lt;br /&gt;
The last two forces mentioned above work against each other, so the challenge for animators is to find a balance. The animation needs to be made with a high enough frame rate to describe the movements the way you want them to appear, yet it needs to be as low a frame rate as possible to avoid having the client discard small movements on upload. It depends on the animation, but a good starting point might be around 10 FPS for the animation. Try a lower animation frame rate if the animation appears ok with fewer frames, relying more on the linear interpolation by the client to smooth it out. Use a higher animation frame rate if the linear interpolation doesn&#039;t look quite right and you need finer resolution to describe more complex or non-linear movements.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Script Memory Limits ==&lt;br /&gt;
Script Memory limits are coming in 2010! Here&#039;s some information to help you prepare;&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Limits_FAQ| FAQ about script memory limits]]&lt;br /&gt;
* [[User:Becky_Pippen/Measure_Script_Memory_Usage|Measuring your script memory usage]]&lt;br /&gt;
* [[User:Becky_Pippen/Memory_Efficiency|Calculating memory needed and memory usage efficiency]]&lt;br /&gt;
* [[User:Becky_Pippen/Script_Memory_Limits| A checklist for scripters -- techniques for reducing memory usage]]&lt;br /&gt;
* [[User:Becky_Pippen/New_LSL_Functions| New LSL functions llGetLinkPrimitiveParams() and llSetLinkPrimitiveParamsFast()]]&lt;br /&gt;
* [[User:Becky_Pippen/Hashing| Scripting techniques for hashing data to conserve memory]]&lt;br /&gt;
* [[User:Becky_Pippen/Numeric_Storage| Scripting techniques for storing arbitrary binary data without lists]]&lt;br /&gt;
* [[User:Becky_Pippen/Text_Storage| Scripting techniques for compressing ASCII text in Mono]]&lt;br /&gt;
* [http://lslwiki.net/lslwiki/wakka.php?wakka=MemoryUsage Empirical measurements of memory usage by data type and scope (lslwiki.net)]&lt;br /&gt;
* [[LSL_Script_Memory|Empirical measurements of memory usage by data type and scope (SL wiki)]]&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
* [http://slurl.com/secondlife/Nevia/128/128/23 The Community of the Nevia Archipelago] [http://www.inkwelle.com Website]&lt;br /&gt;
* [http://jira.secondlife.com/ JIRA Issue Tracker]&lt;br /&gt;
* [https://wiki.secondlife.com/wiki/About_AWG Architecture Working Group]&lt;br /&gt;
* [https://wiki.secondlife.com/wiki/Mono Mono wiki]&lt;br /&gt;
{{skills&lt;br /&gt;
|Builder=*&lt;br /&gt;
|Scripter=*&lt;br /&gt;
|Terraformer=&lt;br /&gt;
|Architect=&lt;br /&gt;
|Scenographer=&lt;br /&gt;
|SLogistician=&lt;br /&gt;
|}}&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Text_Storage&amp;diff=698822</id>
		<title>User:Becky Pippen/Text Storage</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Text_Storage&amp;diff=698822"/>
		<updated>2010-01-06T22:33:13Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: minor tweaks&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Text strings in scripts ==&lt;br /&gt;
&lt;br /&gt;
===Terminology===&lt;br /&gt;
&lt;br /&gt;
Be warned -- these terms are not always used correctly and consistently in the LSL wikis. Here&#039;s the terminology from their various standards:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;[http://en.wikipedia.org/wiki/Unicode Unicode]&#039;&#039;&#039; is a character set of several thousand character glyphs and their assigned numeric ID codes. The numeric codes are 21-bit integers in the range 0 through 0x10ffff, although a few numbers in that range have special meaning other than character data. How are those 21-bit numbers stored in memory? That&#039;s what UTF-8 and UTF-16 are all about.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;[http://en.wikipedia.org/wiki/Utf-8 UTF-8]&#039;&#039;&#039; and &#039;&#039;&#039;[http://en.wikipedia.org/wiki/Utf-16 UTF-16]&#039;&#039;&#039; are two ways to represent the Unicode numeric codes in memory. UTF-8 encodes a Unicode ID number in a variable-length sequence of one to four bytes. UTF-16 encodes most characters in 16 bits, and some in 32 bits.&lt;br /&gt;
&lt;br /&gt;
For a summary of Unicode character set and examples of the UTF-8 and UTF-16 storage formats, see: [[Unicode_In_5_Minutes]].&lt;br /&gt;
&lt;br /&gt;
LSO-compiled scripts store strings internally in memory in UTF-8 format and Mono uses UTF-16, but all that should be transparent to the script for most purposes. The main impact for the script and scripter is the amount of memory used. [[LSL_Script_Memory|This page]] says that globally scoped strings use one byte per character in LSO and two bytes per character in Mono, which is true for strings containing only 7-bit ASCII characters. When the strings contain mostly international characters outside the ASCII range, then the memory consumption for UTF-8 in LSO and UTF-16 in Mono will both be close to two bytes per character, and in some cases the UTF-8 will be longer if there are many characters that require the three-byte or four-byte forms of UTF-8 encoding.&lt;br /&gt;
&lt;br /&gt;
===So, summarize, ok?===&lt;br /&gt;
&lt;br /&gt;
So, if using only ASCII and compiling with Mono, we can possibly reduce the memory requirements to LSO levels through clever encoding. Otherwise any attempt made to compress text will probably use more instruction code space than the amount of memory saved.&lt;br /&gt;
&lt;br /&gt;
===ASCII text compression in Mono===&lt;br /&gt;
&lt;br /&gt;
This technique applies if your script uses only ASCII characters and only if compiling with Mono. We&#039;ll take the ASCII characters, two at a time, convert them to their Unicode numeric ID codes (which for these characters are identical to their ASCII numeric codes), combine them into a 14-bit integer, add a bias that so that the 14-bit numbers all are inside a valid range of Unicode characters, then convert it into a single Unicode character. This will result in a 16-bit Unicode character that bears no resemblance to the two ASCII characters that it encodes.&lt;br /&gt;
&lt;br /&gt;
The decoding process is just the reverse -- convert a Unicode character into its numeric value which will be a 14-bit integer, divide it into two 7-bit numbers, and convert that back to a string of two characters.&lt;br /&gt;
&lt;br /&gt;
For encoding, we need a couple of functions -- one to convert a single Unicode character to its numeric ID, and one to convert a 14-bit number into a single Unicode character. Here&#039;s the former:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Given a single character c, this returns its Unicode ID number&lt;br /&gt;
 // This works only for character codes 0 through 0xffff.&lt;br /&gt;
 //&lt;br /&gt;
 integer charToUnicodeIdNumber(string c)&lt;br /&gt;
     integer cInt = llBase64ToInteger(llStringToBase64(c));&lt;br /&gt;
 &lt;br /&gt;
     if (!(cInt &amp;amp; 0x80000000)) {&lt;br /&gt;
         // UTF-8 single-byte form&lt;br /&gt;
         cInt = cInt &amp;gt;&amp;gt; 24;&lt;br /&gt;
     } else {&lt;br /&gt;
         if ((cInt &amp;amp; 0xe0000000) == 0xc0000000) {&lt;br /&gt;
             // two-byte UTF-8 form:  110v vvvv  10vv vvvv&lt;br /&gt;
             cInt = ((cInt &amp;amp; 0x1f000000) &amp;gt;&amp;gt; 18) |&lt;br /&gt;
                    ((cInt &amp;amp; 0x003f0000) &amp;gt;&amp;gt; 16);&lt;br /&gt;
         } else {&lt;br /&gt;
             // assume three-byte UTF-8 form:  1110 vvvv  10vv vvvv  10vv vvvv&lt;br /&gt;
             cInt = ((cInt &amp;amp; 0x0f000000) &amp;gt;&amp;gt; 12) |&lt;br /&gt;
                    ((cInt &amp;amp; 0x003f0000) &amp;gt;&amp;gt; 10) |&lt;br /&gt;
                    ((cInt &amp;amp; 0x00003f00) &amp;gt;&amp;gt; 8);&lt;br /&gt;
         } // else ignore the 4-byte UTF-8 form&lt;br /&gt;
     } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This works because the function [[llStringToBase64]]() converts the character c into UTF-8 format first, then into base64 encoding. This isn&#039;t documented well, so let&#039;s be clear about it -- regardless if running in LSO where text is UTF-8 or in Mono where text is UTF-16, the function [[llStringToBase64]]() will return a base64-encoded form of the UTF-8 encoding of its argument.&lt;br /&gt;
&lt;br /&gt;
This function is very similar to the function&lt;br /&gt;
&amp;lt;lsl&amp;gt; integer UTF8ToUnicodeInteger(string input); &amp;lt;/lsl&amp;gt;&lt;br /&gt;
found in [[Combined_Library]]. I&#039;m offering my own version here for two reasons: (1) I&#039;ve named the function to emphasize that it takes a single character as string input, and to remove &amp;quot;UTF-8&amp;quot; from the function name because the role of UTF-8 in the function is only as a necessary intermediate encoding. The function is meant to work with LSO or Mono so that the underlying internal encoding of the input argument is transparent; and (2) my version is a little easier to read (at the expense of being a few bytes longer).&lt;br /&gt;
&lt;br /&gt;
And just for completeness, if you need to revise the function to also work with Unicode ID codes above 0xffff, the additional else clause looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; . . .&lt;br /&gt;
 } else {&lt;br /&gt;
     // four-byte UTF-8 form:  1111 0vvv 10vv vvvv 10vv vvvv 10vv vvvv&lt;br /&gt;
     cInt = ((cInt &amp;amp; 0x07000000) &amp;gt;&amp;gt; 6) |&lt;br /&gt;
            ((cInt &amp;amp; 0x003f0000) &amp;gt;&amp;gt; 4) |&lt;br /&gt;
            ((cInt &amp;amp; 0x00003f00) &amp;gt;&amp;gt; 2) |&lt;br /&gt;
             (cInt &amp;amp; 0x0000003f);&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next, we need a way to combine two 7-bit character codes into a single Unicode character. We&#039;ll simply combine two numbers to make a 14-bit integer, then convert that to a Unicode character using the function encode15BitsToChar() found in [[User:Becky_Pippen/Numeric_Storage]]:&lt;br /&gt;
&lt;br /&gt;
For decoding, we&#039;ll use the function decodeCharTo15Bits() found in [[User:Becky_Pippen/Numeric_Storage]] to get our 14-bit number, then split that into two 7-bit numbers, then use [[llUnescapeURL]]() to turn those into two characters in a string.&lt;br /&gt;
&lt;br /&gt;
===Code Example===&lt;br /&gt;
&lt;br /&gt;
First, we need to run a benchmark to measure memory usage in the normal uncompressed way. Simply put a bunch of ASCII text into a notecard and drop it into a prim with this script. It will concatenate all the notecard lines into a single global string named bigText. I used the text from http://www.gutenberg.org/files/6274/6274.txt as the notecard text. It runs out of memory after saving 1063 notecard lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt;&lt;br /&gt;
 integer lineNumber;&lt;br /&gt;
 string notecardName = &amp;quot;ASCII text&amp;quot;;&lt;br /&gt;
 integer linesRead;&lt;br /&gt;
 string bigText;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         llGetNotecardLine(notecardName, lineNumber++);&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     dataserver(key id, string data)&lt;br /&gt;
     {&lt;br /&gt;
         if (data != EOF) {&lt;br /&gt;
             bigText += data;&lt;br /&gt;
             llOwnerSay((string)(++linesRead) + &amp;quot;:&amp;quot; + data);&lt;br /&gt;
             llGetNotecardLine(notecardName, lineNumber++);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we can compare -- here&#039;s the same notecard reader script using ASCII compression. Even though the added compression functions consume about 3K of program space, we can now store 1854 lines. &#039;&#039;&#039;That&#039;s 74% more text using compression.&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Demo of ASCII compression in Mono scripts&lt;br /&gt;
 // By Becky Pippen, 2009, contributed to Public Domain&lt;br /&gt;
 &lt;br /&gt;
 // Report memory free and the change from the last report:&lt;br /&gt;
 //&lt;br /&gt;
 integer lastFree;&lt;br /&gt;
 &lt;br /&gt;
 mem(string label)&lt;br /&gt;
 {&lt;br /&gt;
     integer newFree = llGetFreeMemory();&lt;br /&gt;
     integer change = lastFree - newFree;&lt;br /&gt;
     llOwnerSay((string)newFree + &amp;quot; free, change = &amp;quot; + (string)change);&lt;br /&gt;
     lastFree = newFree;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Converts n = [0..0xff] to two hex characters&lt;br /&gt;
 //&lt;br /&gt;
 string hexChar2(integer n)&lt;br /&gt;
 {&lt;br /&gt;
     string hexChars = &amp;quot;0123456789abcdef&amp;quot;;&lt;br /&gt;
     return llGetSubString(hexChars, n &amp;gt;&amp;gt; 4, n &amp;gt;&amp;gt; 4) +&lt;br /&gt;
            llGetSubString(hexChars, n &amp;amp; 0xf, n &amp;amp; 0xf);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Given a single character c, this returns its Unicode ID number&lt;br /&gt;
 // This works only for character codes 0 through 0xffff.&lt;br /&gt;
 // For a more compact alternative, see UTF8ToUnicodeInteger()&lt;br /&gt;
 // found in http://wiki.secondlife.com/wiki/Combined_Library .&lt;br /&gt;
 //&lt;br /&gt;
 integer charToUnicodeIdNumber(string c)&lt;br /&gt;
 {&lt;br /&gt;
     integer cInt = llBase64ToInteger(llStringToBase64(c));&lt;br /&gt;
 &lt;br /&gt;
     if (!(cInt &amp;amp; 0x80000000)) {&lt;br /&gt;
         // UTF-8 single-byte form&lt;br /&gt;
         cInt = cInt &amp;gt;&amp;gt; 24;&lt;br /&gt;
     } else {&lt;br /&gt;
         if ((cInt &amp;amp; 0xe0000000) == 0xc0000000) {&lt;br /&gt;
             // two-byte UTF-8 form:  110v vvvv  10vv vvvv&lt;br /&gt;
             cInt = ((cInt &amp;amp; 0x1f000000) &amp;gt;&amp;gt; 18) |&lt;br /&gt;
                    ((cInt &amp;amp; 0x003f0000) &amp;gt;&amp;gt; 16);&lt;br /&gt;
         } else {&lt;br /&gt;
             // assume three-byte UTF-8 form:  1110 vvvv  10vv vvvv  10vv vvvv&lt;br /&gt;
             cInt = ((cInt &amp;amp; 0x0f000000) &amp;gt;&amp;gt; 12) |&lt;br /&gt;
                    ((cInt &amp;amp; 0x003f0000) &amp;gt;&amp;gt; 10) |&lt;br /&gt;
                    ((cInt &amp;amp; 0x00003f00) &amp;gt;&amp;gt; 8);&lt;br /&gt;
         } // else ignore the 4-byte UTF-8 form&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return cInt;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // This is a memory-savings technique for use with Mono-compiled LSL scripts.&lt;br /&gt;
 // (It probably works in classic LSO too, but not as efficiently.) This technique&lt;br /&gt;
 // stores 15 bits of information in each 16-bit Unicode character. Use the&lt;br /&gt;
 // encode function below to convert any 15-bit data to a Unicode character, and&lt;br /&gt;
 // use the decode function to convert it back to the original 15-bit data.&lt;br /&gt;
 //&lt;br /&gt;
 // This example maps the data values 0 through 0x7fff to the Unicode&lt;br /&gt;
 // characters U-001000 through U-008fff. Use the matching function&lt;br /&gt;
 // decodeCharTo15Bits() to decode the Unicode character back into the original&lt;br /&gt;
 // 15-bit number.&lt;br /&gt;
 //&lt;br /&gt;
 // The technique used here is very similar to the technique used in the &amp;quot;Base 32768&lt;br /&gt;
 // Script&amp;quot; in http://wiki.secondlife.com/wiki/Key_Compression .&lt;br /&gt;
 &lt;br /&gt;
 // Convert any 15-bit integer into a single Unicode character&lt;br /&gt;
 //&lt;br /&gt;
 string encode15BitsToChar(integer num)&lt;br /&gt;
 {&lt;br /&gt;
     // Check the incoming range&lt;br /&gt;
 &lt;br /&gt;
     if (num &amp;lt; 0 || num &amp;gt;= 0x8000) {&lt;br /&gt;
         // illegal input -- do whatever is appropriate&lt;br /&gt;
         return &amp;quot;�&amp;quot;;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     // Bias the incoming numeric value by 0x1000 to avoid illegal Unicode codes:&lt;br /&gt;
 &lt;br /&gt;
     num += 0x1000;&lt;br /&gt;
 &lt;br /&gt;
     // Construct an escaped hex UTF-8 representation and return&lt;br /&gt;
     // it as a Unicode character&lt;br /&gt;
 &lt;br /&gt;
     return llUnescapeURL(&lt;br /&gt;
                   &amp;quot;%&amp;quot; + hexChar2(0xe0 + (num &amp;gt;&amp;gt; 12)) +&lt;br /&gt;
                   &amp;quot;%&amp;quot; + hexChar2(0x80 + ((num &amp;gt;&amp;gt; 6) &amp;amp; 0x3f)) +&lt;br /&gt;
                   &amp;quot;%&amp;quot; + hexChar2(0x80 + (num &amp;amp; 0x3f)));&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 // This is the inverse of encode15BitsToChar(), supra, q.v.&lt;br /&gt;
 // This expects a single 16-bit Unicode character that was created by&lt;br /&gt;
 // encode15BitsToChar() and returns the 15-bit numeric value used to create it.&lt;br /&gt;
 // The 15-bit return value will always be in the range 0x0000 - 0x7fff.&lt;br /&gt;
 //&lt;br /&gt;
 integer decodeCharTo15Bits(string ch)&lt;br /&gt;
 {&lt;br /&gt;
     string utf8 = llEscapeURL(ch); // convert to escaped hex UTF-8&lt;br /&gt;
 &lt;br /&gt;
     return&lt;br /&gt;
         (((integer)(&amp;quot;0x&amp;quot; + llGetSubString(utf8, 1, 2)) &amp;amp; 0x1f) &amp;lt;&amp;lt; 12) +&lt;br /&gt;
         (((integer)(&amp;quot;0x&amp;quot; + llGetSubString(utf8, 4, 5)) &amp;amp; 0x3f) &amp;lt;&amp;lt; 6) +&lt;br /&gt;
          ((integer)(&amp;quot;0x&amp;quot; + llGetSubString(utf8, 7, 8)) &amp;amp; 0x3f) - 0x1000;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 // Returns a Unicode string that encodes twice as many ASCII characters.&lt;br /&gt;
 // Use the matching function decompressAscii() to expand it back into&lt;br /&gt;
 // the original ASCII.&lt;br /&gt;
 //&lt;br /&gt;
 string compressAscii(string s)&lt;br /&gt;
 {&lt;br /&gt;
     integer len = llStringLength(s);&lt;br /&gt;
 &lt;br /&gt;
     // Append a space if needed to make s an even number of chars&lt;br /&gt;
     if (len % 2) {&lt;br /&gt;
        s += &amp;quot; &amp;quot;;&lt;br /&gt;
        ++len;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     string encodedChars;&lt;br /&gt;
     integer i;&lt;br /&gt;
     for (i = 0; i &amp;lt; len; i += 2) {&lt;br /&gt;
         encodedChars += encode15BitsToChar(&lt;br /&gt;
                 charToUnicodeIdNumber(llGetSubString(s, i, i)) &amp;lt;&amp;lt; 7 |&lt;br /&gt;
                 charToUnicodeIdNumber(llGetSubString(s, i+1, i+1)));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return encodedChars;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // This is the inverse of compressAscii()&lt;br /&gt;
 //&lt;br /&gt;
 string uncompressAscii(string s)&lt;br /&gt;
 {&lt;br /&gt;
     string result;&lt;br /&gt;
 &lt;br /&gt;
     integer len = llStringLength(s);&lt;br /&gt;
     integer i;&lt;br /&gt;
     for (i = 0; i &amp;lt; len; ++i) {&lt;br /&gt;
         integer cInt15 = decodeCharTo15Bits(llGetSubString(s, i, i));&lt;br /&gt;
         result += llUnescapeURL(&amp;quot;%&amp;quot; + hexChar2(cInt15 &amp;gt;&amp;gt; 7) +&lt;br /&gt;
                                 &amp;quot;%&amp;quot; + hexChar2(cInt15 &amp;amp; 0x7f));&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return result;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 integer lineNumber;&lt;br /&gt;
 string notecardName = &amp;quot;ASCII text&amp;quot;;&lt;br /&gt;
 integer linesRead;&lt;br /&gt;
 string bigText;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         llGetNotecardLine(notecardName, lineNumber++);&lt;br /&gt;
     }&lt;br /&gt;
     &lt;br /&gt;
     dataserver(key id, string data)&lt;br /&gt;
     {&lt;br /&gt;
         if (data != EOF) {&lt;br /&gt;
             string compressedLine = compressAscii(data);&lt;br /&gt;
             bigText += compressedLine;&lt;br /&gt;
             llOwnerSay((string)(++linesRead) + &amp;quot;:&amp;quot; + uncompressAscii(compressedLine));&lt;br /&gt;
             llGetNotecardLine(notecardName, lineNumber++);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Also see===&lt;br /&gt;
&lt;br /&gt;
For more ideas about compressing bits into Unicode strings, see&lt;br /&gt;
* [[User:Becky_Pippen/Key_Storage| Scripting techniques for storing lots of keys (UUIDs)]]&lt;br /&gt;
* [[User:Becky_Pippen/Numeric_Storage| Scripting techniques for storing arbitrary binary data without lists]]&lt;br /&gt;
* [[User:Becky_Pippen/Script_Memory_Limits| Memory limits FAQ and Checklist for memory reduction]]&lt;br /&gt;
* [[User:Becky_Pippen/Hashing| Hashing for memory reduction]]&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
	<entry>
		<id>https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Hashing&amp;diff=698812</id>
		<title>User:Becky Pippen/Hashing</title>
		<link rel="alternate" type="text/html" href="https://wiki.secondlife.com/w/index.php?title=User:Becky_Pippen/Hashing&amp;diff=698812"/>
		<updated>2010-01-06T22:28:00Z</updated>

		<summary type="html">&lt;p&gt;Becky Pippen: minor tweaks&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Hashing to conserve memory==&lt;br /&gt;
&lt;br /&gt;
Hashing takes a longer item like an avatar name or [[UUID]] and maps it to something smaller, like an integer or a shorter string. It&#039;s compact, but the trade-off is that different inputs can possibly map to the same hash value by accident.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an illustration of how much memory we can save by hashing UUIDs in a Mono script -- from 72 bytes in their native form to just a few bytes each. This illustrates techniques discussed in the rest of this article:&lt;br /&gt;
&lt;br /&gt;
[[Image:Key hash-1.jpg|800px]]&lt;br /&gt;
&lt;br /&gt;
There are two problems to solve to make hashing work in LSL: (1) designing a hash function that makes hash codes of the right size, evenly distributed across the hash space, and (2) storing the hash codes efficiently without suffering the overhead of lists. Those two aspects are treated separately below.&lt;br /&gt;
&lt;br /&gt;
===Designing the hash function===&lt;br /&gt;
&lt;br /&gt;
A hash size too small increases the chance of a hash collision. That&#039;s when multiple values of the real data map to the same hash value. A hash size too large wastes memory.&lt;br /&gt;
&lt;br /&gt;
Calculating the probability of hash collisions is similar to calculating the [http://betterexplained.com/articles/understanding-the-birthday-paradox/ &amp;quot;Birthday Paradox&amp;quot; probabilities]. The probability that there will be a duplicate among N random hashes of W bits each is approximately:&lt;br /&gt;
&lt;br /&gt;
 P = 1.0 - e ^ -(N^2 / (2 * 2^W))&lt;br /&gt;
&lt;br /&gt;
The table below shows some examples of the probability &#039;&#039;P&#039;&#039; of having at least one duplicate among &#039;&#039;N&#039;&#039; hashes of various sizes. For example, there is a probability of 0.0001 that there will be a duplicate in a set of 1000 32-bit hash codes.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot;&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|15-bit&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|16-bit&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|30-bit&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|32-bit&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|45-bit&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | &#039;&#039;N&#039;&#039;&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | &#039;&#039;P&#039;&#039;&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | &#039;&#039;P&#039;&#039;&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | &#039;&#039;P&#039;&#039;&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | &#039;&#039;P&#039;&#039;&lt;br /&gt;
|align=&amp;quot;center&amp;quot; | &#039;&#039;P&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 100&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.14&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.07&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.000005&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.000001&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.0000000001&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 200&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.46&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.26&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.00002&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.000005&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.0000000006&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 500&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.978&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.85&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.0001&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.00003&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.0000000004&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 1000&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.9995&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.0005&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.0001&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.00000001&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 2000&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.001&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.0005&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.00000006&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 5000&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.01&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.003&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.0000007&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 10000&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.05&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.01&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.000001&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 25000&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.26&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.07&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.000009&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;right&amp;quot; | 50000&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 1.0&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.69&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.25&lt;br /&gt;
|align=&amp;quot;left&amp;quot; | 0.00004&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====Hashing UUIDs====&lt;br /&gt;
&lt;br /&gt;
It appears that most [[UUID]]s in SL are constructed with random bits (or sufficiently so for our purposes) in the first six bytes (first 12 hex digits) of the UUID. This lets us make a trivial hash function by extracting some of the hex digits from the UUID. For example, this code extracts the first eight hex digits as a hash:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; string hash(key k)&lt;br /&gt;
 {&lt;br /&gt;
     return llGetSubString((string)k, 0, 7); // first 8 hex chars&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt; &lt;br /&gt;
&lt;br /&gt;
That gives us a hash code with 32 bits of information content (eight hex characters at four bits each).&lt;br /&gt;
&lt;br /&gt;
At this point you might wonder why not reduce the hash code to a regular 32-bit integer that only uses four bytes of memory. Unfortunately,  we can&#039;t do anything with integers except store them in a list, and that makes any string-based solution more efficient, as described below.&lt;br /&gt;
&lt;br /&gt;
If you have any concern that the first few bytes of the UUID are not really as random as you would like, you can make an MD5 hash out of the UUID, then take hex digits from the MD5 hash string. For example, this hash function extracts eight hex digits from the MD5 hash of the UUID:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; string hash(key k)&lt;br /&gt;
 {&lt;br /&gt;
     return llGetSubString(llMD5String((string)k, salt), 0, 7);&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Any salt value works in the code above, as long as it&#039;s consistent. Choose an unguessable value for salt if you want your hashing function to yield different values than other scripts using the same code snippet above. Think of the salt value as serving a purpose similar to the seed value used in pseudo-random number generators.&lt;br /&gt;
&lt;br /&gt;
We can make the hash code even smaller by treating the eight hex digits as a 32-bit numeric value and converting that number into a six-character string using [http://en.wikipedia.org/wiki/Base64 base64] representation. Something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; string hash(key k)&lt;br /&gt;
 {&lt;br /&gt;
     return llGetSubString(llIntegerToBase64(&lt;br /&gt;
         (integer)(&amp;quot;0x&amp;quot; + llGetSubString((string)k, 0, 7))), 0, 5);&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In other words, the same information contained in eight hex characters fits into six base64 characters.&lt;br /&gt;
&lt;br /&gt;
====Hashing avatar names and other strings====&lt;br /&gt;
&lt;br /&gt;
Almost anything can be hashed using [[llMD5String]](). That&#039;s a function historically used for encryption and security, and it&#039;s useful for our hashing needs because it nicely distributes all possible input values across its output range. The MD5 conversion gives us a long string of hex characters, and we can simply extract however many we want to use for a hash. The longer the hash, the less likely there will be a hash collision but the more memory it will consume. This example extracts twelve hex characters from the MD5 result to form a hash code worth 48 bits of information:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; string hash(string input)&lt;br /&gt;
 {&lt;br /&gt;
     string md5 = llMD5String(input, salt);&lt;br /&gt;
     return llGetSubString(md5, 0, 11);&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt; &lt;br /&gt;
&lt;br /&gt;
The problem with this hash function is that the average avatar name in SL is about 14 characters, so a twelve-character hash code is not a dramatic improvement. And then if we store these hash codes in a list, we eat up additional memory with list overhead. To save memory, we need to think of these hash codes as containing so many bits of information, not as characters. Then we need to think of a way to store those bits of information in a more compact form, i.e., without using lists. The next section describes a way of encoding information into Unicode character strings.&lt;br /&gt;
&lt;br /&gt;
===Avoiding list overhead===&lt;br /&gt;
&lt;br /&gt;
It&#039;s very difficult to store data compactly in Mono scripts -- there are no arrays; lists incur 20+ bytes of overhead per list element; and string data uses a minimum of two bytes per character. About the only way we have to encode arbitrary data in memory is by encoding the information as Unicode characters.&lt;br /&gt;
&lt;br /&gt;
Using the functions in [[User:Becky_Pippen/Numeric_Storage| this article]], you can store 15 bits of arbitrary data in each 16-bit Unicode character. It&#039;s not quite as straightforward as interpreting the 15 bits as the Unicode character ID because certain Unicode character codes may not be used. For example, we can&#039;t simply use the Unicode character ID 0x000000 to store a numeric value of zero, because 0x000000 is an illegal Unicode character code. So we have to remap any 15-bit number we want to store into some range of legal Unicode characters. The functions [[User:Becky_Pippen/Numeric_Storage#Source_code_example|encode15BitsToChar()]] and [[User:Becky_Pippen/Numeric_Storage#Source_code_example|decodeCharTo15Bits()]] do this remapping by adding 0x1000 to the 15-bit number to make a Unicode character ID in the range 0x1000 through 0x8fff. The result is that a 15-bit hash code can be stored as one character; a 30-bit hash code in two characters, and a 45-bit hash code in three Unicode characters.&lt;br /&gt;
&lt;br /&gt;
===Searching for hash codes when concatenated in a string===&lt;br /&gt;
&lt;br /&gt;
If the consequence of a false positive match is an acceptable risk, then searching a long string for a given hash code can be simplified to a call to [[llSubStringIndex]](). In this example, hash codes are 45-bit values encoded as three Unicode characters each. All hash codes are concatenated into one long string named hashCodes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Returns logical index of the given hash code&lt;br /&gt;
 // or -1 if not found&lt;br /&gt;
 &lt;br /&gt;
 string hashCodes;  // all hash codes are concatenated here&lt;br /&gt;
 &lt;br /&gt;
 integer findHash(string hashCode)&lt;br /&gt;
 {&lt;br /&gt;
     integer index = llSubStringIndex(hashCodes, hashCode);&lt;br /&gt;
     if (index != -1) {&lt;br /&gt;
         index /= 3;  // because each hash code is three Unicode chars&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return index;&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The code above will find a false match if the hashCode value accidentally randomly matches characters that span two different hash codes in the hashCodes string. Your script can detect false positives by checking if the match occurred on a boundary of hashSize bytes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Returns logical index of the given hash code&lt;br /&gt;
 // or -1 if not found&lt;br /&gt;
 &lt;br /&gt;
 string hashCodes;  // all hash codes are concatenated here&lt;br /&gt;
 integer hashSize = 6; // number of chars in each hash code&lt;br /&gt;
 &lt;br /&gt;
 integer findHash(string hashCode)&lt;br /&gt;
 {&lt;br /&gt;
     integer index = llSubStringIndex(hashCodes, hashCode);&lt;br /&gt;
     if (index != -1 &amp;amp;&amp;amp; (index % hashSize)) {&lt;br /&gt;
         // this is a false match.&lt;br /&gt;
         // fall back to a loop search, or whatever is appropriate&lt;br /&gt;
     } else if (index != -1) {&lt;br /&gt;
         // a valid match&lt;br /&gt;
         index /= hashSize;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     return index;&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Code Example===&lt;br /&gt;
&lt;br /&gt;
Here is a complete example of a script that stores avatar names read from a notecard. First let&#039;s get a benchmark of the memory consumption using the naive approach of storing the names in a global list. The notecard I used for a test contains 2000 actual SL avatar names, one per line. There are only 1500 unique names in the notecard, so 500 of them are duplicates.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; integer lineNumber;&lt;br /&gt;
 string notecardName = &amp;quot;names&amp;quot;;&lt;br /&gt;
 integer namesSaved;&lt;br /&gt;
 list hashedNames;&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         llGetNotecardLine(notecardName, lineNumber++);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     dataserver(key id, string avName)&lt;br /&gt;
     {&lt;br /&gt;
         if (avName != EOF) {&lt;br /&gt;
             if (llListFindList(hashedNames, [avName]) == -1) {&lt;br /&gt;
                 hashedNames += [avName];&lt;br /&gt;
                 llOwnerSay((string)(++namesSaved) + &amp;quot;: &amp;quot; + avName + &amp;quot;, &amp;quot; +&lt;br /&gt;
                         (string)llGetFreeMemory() + &amp;quot; free&amp;quot;);&lt;br /&gt;
             } else {&lt;br /&gt;
                 llOwnerSay(&amp;quot;duplicate: &amp;quot; + avName);&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             llGetNotecardLine(notecardName, lineNumber++);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The data ate up 56000 bytes of available memory and ran out of memory after saving 1137 unique names. That works out to about 49 bytes consumed per item. That&#039;s roughly what we expected in Mono, based on the data in [[LSL_Script_Memory|this page]] -- an average of 14 characters per avatar name uses 28 bytes of memory in Mono, plus 22 bytes of overhead per list element for a total of 14+28=42 bytes each.&lt;br /&gt;
&lt;br /&gt;
Here is the same script but with the functions needed to save the avatar identities as 45-bit hash codes encoded as three Unicode characters each. It stores 1500 hashed names in just 9000 bytes. Even adding the 2500 extra bytes of program code to support hashing, that comes out to a total 11500 bytes consumed to store 1500 avatar identities, or 7.7 bytes each, compared to 49 bytes each on the average for the naive list approach above. &#039;&#039;&#039;That&#039;s an 84% net reduction in memory consumption.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;lsl&amp;gt; // Demo - store avatar identities as 45-bit hases encoded in 3 Unicode&lt;br /&gt;
 // chars each, concatenated into one string.&lt;br /&gt;
 // By Becky Pippen, 2010, contributed to the public domain.&lt;br /&gt;
 &lt;br /&gt;
 integer lastFree;&lt;br /&gt;
 &lt;br /&gt;
 // Report memory free and the change from the last report:&lt;br /&gt;
 //&lt;br /&gt;
 mem(string label)&lt;br /&gt;
 {&lt;br /&gt;
     integer newFree = llGetFreeMemory();&lt;br /&gt;
     integer change = lastFree - newFree;&lt;br /&gt;
     llOwnerSay((string)newFree + &amp;quot; free, change = &amp;quot; + (string)change);&lt;br /&gt;
     lastFree = newFree;&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // Converts n = [0..0xff] to &amp;quot;%&amp;quot; + two hex characters&lt;br /&gt;
 //&lt;br /&gt;
 string escapeHexChar(integer n)&lt;br /&gt;
 {&lt;br /&gt;
     string hexChars = &amp;quot;0123456789abcdef&amp;quot;;&lt;br /&gt;
     return &amp;quot;%&amp;quot; +&lt;br /&gt;
           llGetSubString(hexChars, n &amp;gt;&amp;gt; 4, n &amp;gt;&amp;gt; 4) +&lt;br /&gt;
           llGetSubString(hexChars, n &amp;amp; 0xf, n &amp;amp; 0xf);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 // Encode 15 bits of data as a single Unicode character. Use decodeCharTo15Bits()&lt;br /&gt;
 // to recover the 15-bit data.&lt;br /&gt;
 //&lt;br /&gt;
 // The technique used here is very similar to the technique used in the &amp;quot;Base 32768&lt;br /&gt;
 // Script&amp;quot; in http://wiki.secondlife.com/wiki/Key_Compression .&lt;br /&gt;
 &lt;br /&gt;
 string encode15BitsToChar(integer num)&lt;br /&gt;
 {&lt;br /&gt;
     // Use only the lower 15 bits and bias by 0x1000 to avoid illegal Unicode IDs:&lt;br /&gt;
 &lt;br /&gt;
     num = 0x1000 + (num &amp;amp; 0x00007fff);&lt;br /&gt;
 &lt;br /&gt;
     // Construct an escaped hex UTF-8 representation and&lt;br /&gt;
     // return it as one Unicode character:&lt;br /&gt;
 &lt;br /&gt;
     return llUnescapeURL(&lt;br /&gt;
               escapeHexChar(0xe0 + (num &amp;gt;&amp;gt; 12)) +&lt;br /&gt;
               escapeHexChar(0x80 + ((num &amp;gt;&amp;gt; 6) &amp;amp; 0x3f)) +&lt;br /&gt;
               escapeHexChar(0x80 + (num &amp;amp; 0x3f)));&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 //****************************************&lt;br /&gt;
 // demo -- store avatar identities as compressed 45-bit hashes of their name&lt;br /&gt;
 &lt;br /&gt;
 integer lineNumber;       // for the notecard reader&lt;br /&gt;
 string notecardName = &amp;quot;names&amp;quot;; // notecard of av names, one per line&lt;br /&gt;
 integer namesSaved;       // count how many successfully saved&lt;br /&gt;
 string hashedNames;       // all the hashes get concatenated in this string&lt;br /&gt;
 integer hashSize = 3;     // num of Unicode chars per encoded hash code&lt;br /&gt;
 &lt;br /&gt;
 default&lt;br /&gt;
 {&lt;br /&gt;
     touch_start(integer num)&lt;br /&gt;
     {&lt;br /&gt;
         llGetNotecardLine(notecardName, lineNumber++);&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     dataserver(key id, string avName)&lt;br /&gt;
     {&lt;br /&gt;
         if (avName != EOF) {&lt;br /&gt;
             // Pick off 48 bits of information to start with:&lt;br /&gt;
             string md5 = llMD5String(avName, 0);&lt;br /&gt;
             integer n1 = (integer)(&amp;quot;0x&amp;quot; + llGetSubString(md5, 0, 7));&lt;br /&gt;
             integer n2 = (integer)(&amp;quot;0x&amp;quot; + llGetSubString(md5, 8, 11));&lt;br /&gt;
 &lt;br /&gt;
             // Map 45 of the bits into three characters. We cannot encode Unicode&lt;br /&gt;
             // directly, so we have to use UTF-8 as an intermediate form&lt;br /&gt;
             //&lt;br /&gt;
             // |------------------ n1 ------------------|  |------- n2 -------|&lt;br /&gt;
             // x000 0000  0000 0000  x000 0000  0000 0000  x000 0000  0000 0000&lt;br /&gt;
             //  |------char1------|   |------char2------|   |------char3------|&lt;br /&gt;
 &lt;br /&gt;
             string encoded3Chars = &lt;br /&gt;
                 encode15BitsToChar(n1 &amp;gt;&amp;gt; 16) +&lt;br /&gt;
                 encode15BitsToChar(n1) +&lt;br /&gt;
                 encode15BitsToChar(n2);&lt;br /&gt;
 &lt;br /&gt;
             integer idx = llSubStringIndex(hashedNames, encoded3Chars);&lt;br /&gt;
             if (idx == -1) {&lt;br /&gt;
                 // this is a new one, so save it&lt;br /&gt;
                 hashedNames += encoded3Chars;&lt;br /&gt;
                 llOwnerSay((string)(++namesSaved) + &amp;quot;: &amp;quot; + avName + &amp;quot;, &amp;quot; +&lt;br /&gt;
                            (string)llGetFreeMemory() + &amp;quot; free&amp;quot;);&lt;br /&gt;
             } else if (idx % hashSize) {&lt;br /&gt;
                 llOwnerSay(&amp;quot;WARNING!!! False match for &amp;quot; + avName);&lt;br /&gt;
             } else {&lt;br /&gt;
                 llOwnerSay(&amp;quot;Duplicate, already saved: &amp;quot; + avName);&lt;br /&gt;
             }&lt;br /&gt;
 &lt;br /&gt;
             llGetNotecardLine(notecardName, lineNumber++);&lt;br /&gt;
         }&lt;br /&gt;
     }&lt;br /&gt;
 } &amp;lt;/lsl&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Also see===&lt;br /&gt;
&lt;br /&gt;
For more ideas about compressing bits into Unicode strings, see&lt;br /&gt;
* [[User:Becky_Pippen/Numeric_Storage| Scripting techniques for storing arbitrary binary data without lists]]&lt;br /&gt;
* [[User:Becky_Pippen/Script_Memory_Limits| Memory limits FAQ and Checklist for memory reduction]]&lt;br /&gt;
&lt;br /&gt;
If you need to store a lot of keys without losing any bits, see the suggestions at [[Key_Compression]].&lt;/div&gt;</summary>
		<author><name>Becky Pippen</name></author>
	</entry>
</feed>