text:
Category: Greg Stoda
Big questions on Day 2 of The Masters
8:03 am Apr. 9, 2010
The ad with the dead father's voice is old news now (kinda icky, though) and Tiger Woods has reintroduced himself to golf with a sparkly four-under-par 68 at Augusta National in the first round of The Masters.
The questions of the day Friday in the second round are these:
Does 50-year-old leader Fred Couples have top-of-the-board staying power?
Does chaser Tom Watson?
Jack and Arnie get Masters started
AUGUSTA, Ga. — Jack Nicklaus and Arnold Palmer – Masters legends of the highest order – served as honorary starters for the tournament Thursday morning at Augusta National.
The 70-year-old Nicklaus, …
Masters chairman scolds Tiger Woods
2:13 pm Apr. 7, 2010
AUGUSTA, Ga. _ Augusta National and Masters chairman Billy Payne addressed the issue of Tiger Woods' personal life Wednesday afternoon during a media session, and was harsh in assessment of the four-time champion's off-course behavior.
Woods has been engulfed in a sex scandal since news of his marital infidelities broke in late November in the wake of a single-vehicle accident in which he was involved.
Tiger Woods will move ratings needle
The first-round Tiger Woods tee time _ 1:42 p.m. on Thursday _ sure does fit nicely with the ESPN television schedule for The Masters.
The network will get a live cut-in on Woods' first drive at Augusta National, and then go full coverage beginning at 4 p.m. in a time slot that should afford it a view of Woods' entire back nine. The general …
Jack and Arnie together again at Augusta National
Jack Nicklaus will join Arnold Palmer in hitting the ceremonial first drives Thursday (7:40 a.m.) to open The Masters at Augusta National.
It will be Nicklaus' first appearance in the role.
"When I was first asked to do it, I was still playing," Nicklaus said. "I had no desire to do that."
Nicklaus now hasn't played in the tournament since 2005, …
Raymond Floyd calls it a career at The Masters
Raymond Floyd announced that he's ending his participation in The Masters after 45 appearances, including a victory in 1976.
"I don't feel like it's the end of an era," said the 67-year-old Floyd. "I didn't want to go out and embarrass myself or play the best and make a whole lot of putts so I could shoot in the 70s."
Floyd, a Palm …
Tiger Woods goes about his business
Tiger Woods teed off at 8 o'clock Tuesday morning for a practice round with Mark O'Meara.
And all was quiet outside the Augusta National gates.
There were no lurking non-credentialed media representatives to be seen chasing Woods-related stories. In fact, it was pretty much business as usual for a Tuesday at The Masters with players doing concentrated prep work on their games ahead of …
Tiger Woods sticks pretty much to the script
You were expecting something different?
It was an advertisement by Tiger Woods promising changed behaviors. Nothing wrong with that.
But it was just a variation on the old theme of an act of contrition. He did what …
Masters fans embrace Tiger Woods
10:26 am Apr. 5, 2010
If he was looking for a figurative hug — and that's undoubtedly what Tiger Woods was doing Monday morning at Augusta National — he got it from Masters fans from the moment he stepped onto …
It's Tiger Monday at The Masters
The gates at Augusta National opened for public entry a few minutes early Monday morning _ a couple of clicks before 8 o'clock, that is _ and patrons quickly sought positions on the course for Tiger Woods' anticipated practice round. He went off in the company of Fred Couples, and hooked his first tee shot before reloading and finding the fairway.
There were encouraging shouts …
Results 11 - 20 of 80	previous next


Salty Crunchy Cravings on a raw vegan diet?
I used to get terrible cravings for salty, crunchy foods on my fruit-based low-fat raw vegan diet. So much so that I would binge on crackers and chips, and this would completely derail me.
Cravings generally mean that you're body is missing something in terms of nutrients. If you don't have one already I put together this free one page Raw Vegan Balanced Nutrition Checklist, which you can use to make sure you're getting all your nutrient needs met on a raw vegan diet.
But basically, if you're not eating enough greens in your diet you're likely to crave salty foods because what you're really craving is sodium.
Greens like celery, cucumbers, zucchinis, leafy green veggies are very high in sodium so try to include them at most of your meals. Greens combine beautifully with fruits of all kinds. Put them in smoothies, juices or simply mix them in with your fruits in a salad.
Sometimes simply by adding a little tamari or miso to your salad dressing you can really satisfy those cravings for salt. I have found that including a very small amount of these types of foods has allowed me to stay on track eating my high raw vegan diet. I used to try to avoid them altogether and it just ended up making me crazy for unhealthy cooked salty vegan foods.
To satisfy the craving for crunchy foods, try mixing up a big bowl of chopped celery and apple.
How have you handled your cravings? Let me know your thoughts on this topic below. Feel free to ask any questions too!
can you please talk about what veggies and fruits go together at one setting. like you explained celery and apples. can you give like a plan for mixing together. thank you so much. like I learned the other day, melons should be eaten alone. is that true?


Despite not being the type of woman to gush over clothes, I love me some Say Yes to the Dress. Yesterday I did some preliminary wedding dress shopping with my mother, grandmother and Colby's mom.
It was… not what I expected. And I mean that in a good way.
A wise friend on Twitter (*waves frantically at the fantastic Lady E*) warned me about wedding dress shopping, comparing wedding dresses to cocaine.
She was not wrong. It was pretty great.
The first dress I tried on was a big, poofy mass of layers and white fabric. Because they didn't have any tea-length dresses, the fabulous consultant clipped and pinned to mimic what a poofy/short style dress would look like on my short, curvy frame.
I kept laughing because I looked like an onion. Then I left the change room and laughed when my companions looked horrified. It was AWFUL. Heavy, cumbersome and unflattering as hell. I immediately felt a twinge of disappointment. My ideal dress was not going to be the one after all! I was warned of such things but I thought for sure that style would be the way to go for me. But no, no, NO. It was not.
The next dress I tried on was more of a beach style dress with ruching around the bust and just a hint of sparkle. It looked pretty on the rack but I just assumed it would be unflattering on me and my round hips and large bust.
And it looked really, really, really good. It was beautiful. I looked like a bride- a comfortable bride that could move without restriction. And it was within budget. I immediately pictured myself at my wedding, wearing that dress, the white chiffon catching the breeze at our outdoor wedding in the country.
And then I cried. And Mom and Grandma did too. Even Colby's mom got tears in her eyes. And then the consultant plucked a veil from the back room and tucked it into my hair. More crying. It was an astonishing moment I didn't expect to have. I always thought that part on Say Yes to the Dress was exaggerated. It was just so overwhelming!
But then that was only the second wedding dress I'd ever put on my body. That is basically the only thing that stopped my credit card from flying out of my purse.
I tried on a few more and didn't get nearly the same reaction. One of the other dresses was covered in lace and was gorgeous. Really very nice. But it was someone else's dress. It certainly didn't make me cry.
I've been thinking about that beach-style dress ever since. I took a photo of myself wearing it at the bridal shop and I keep going back to it. I'm planning to check out one more store (maybe two stores??) and see if I get that same emotional response from any other dress. But I'm not sure if I see that happening.
So, what did I learn from this experience? Go into the store with photos of what you have in mind, try a dress on, realize the image you have in your mind is fine but not great. Then find a different dress that blows all of your expectations out of the water.


Quality Car Sales Suffolk, our vehicles are prepared to the highest standard & are sold with extensive warranties.
We can also source new vehicles to order.
We specialise in finding your ideal vehicle, just let us know as much detail as possible (model, spec, colour etc) & we will source & prepare your choice to the highest standard.
Let us take the hassle of searching away!


Gonna be inactive for a bit, active sometimes on the forums mostly because Thanks Giving is coming up and I just wanna be with the family.
I know it's so heartbreaking, but I must. I know I've been inactive lately before but . Promise to be on the Murder servers more.
Have fun eating fat turkey.


Because it can disrupt a dog's heart rate, dogs with severe hyperkalemia require emergency veterinary attention.
Dogs diagnosed with hyperkalemia have too much potassium in their blood. It almost always affects canines already suffering from kidney disease. Occasionally, it results from urinary tract blockage or bladder rupture. Because high potassium levels affect the heart, hyperkalemia is a veterinary emergency.
Hyperkalemia in canines is primarily caused by not excreting sufficient urine. Properly functioning kidneys help a dog eliminate potassium. Once the dog stops peeing enough and not getting rid of excess potassium through the urine, hyperkalemia can develop within two days. Although it's also possible that a dog can develop hyperkalemia from potassium over-supplementation, that's a rare cause of the disease.
Dogs suffering from hyperkalemia might experience off-and-on gastrointestinal issues such as vomiting and diarrhea. More obvious symptoms include lethargy and outright collapse. The dog's heart rate slows considerably because excess potassium disrupts his blood circulation. A dog can show signs of paralysis, but his limbs remain flexible, not stiff, even if the animal can't move them. A dog might have difficulty urinating, or produce very little pee.
Your vet will perform an electrocardiographic test, or ECG, on your dog to determine the heart rate. She'll also take your dog's complete health history, but if your pet's already under treatment for kidney disease, your vet will have much of that information. She'll take urine and blood samples. If your dog strains to pee, your vet might conduct an ultrasound to determine if there's an obstruction in his urinary tract.
Your vet will lower the amount of potassium in your dog's bloodstream via intravenous saline solution. In severe cases, the potassium might be removed from the bloodstream through dialysis. Once the crisis is over, you'll have to bring your dog to the vet regularly to have his blood potassium levels checked. If your dog is under treatment for kidney disease, your vet has probably already prescribed a prescription diet for him. If hyperkalemia becomes an issue, your vet might prescribe a canine kidney diet with even less potassium.
If your dog goes to the vet and his blood work returns from the lab with an off-the-charts level of potassium, don't panic, especially if your dog appears fine. The test results could stem from pseudohyperkalemia, a condition occurring when excess potassium leaks from the cells after blood drawing. It's a common occurrence in akitas and shar-peis. Dogs actually suffering from hyperkalemia usually appear sick.


The TAYLOR - Rust Second Skin. Our Barely There, timeless triangle bikini top with mid-seam detailing and no neck tie marks, designed with our signature skinny straps for the girl that wants minimal tan lines! The HUNTER - Rust Second Skin. Our moderate coverage bikini bottom, can be worn mid or higher on the hip! Seamless and hardware free. Lined with our signature SIE Rose Gold lining. Moderate coverage bottoms. Model Specs: 32 in Hips, 25 in Waist, 34 in Bust. She's wearing a size Small.


Inside eighteen appearances, this is Emre do's very first objective for the Germany. The 23-year-old appreciated per well round execution in No.6 function to 86 percentage go through precision completing 126 passes.Can likewise prepared three fruitful handles to experienced above 130 touches regarding the ball. Should the website holder wish to do this, they may next test out beyond adjustments of the same CTA, with an identical plan. To if satisfied with their CTA, proceed to features plus areas of the website. Selected luxury stores are nearly always perhaps producing a web business to provide people offers throughout their remedys. Created text attempting to sell try a sure ways until is obviously performed. Direct to the point texting generally sent with mail to be able to men and women, as well as so they remains advisable a selective invite in a amazing selling event. This particular type of campaigning do two things in the correct manner. The concept encourages marketing their scarcity of this trademark with specific invites. You'll find it greater produces brand equity in utilizing somebody their unique greatest personalised component, all smart phone. In concept, divide evaluating could be used to boost the effectiveness to anything regarding any kind of site. But, there are certain highlights as well as fixtures additionally subjected to divide evaluating, which include. In an everyday circumstances, split up screening might be regularly decide the best sized, shape, layout as well as position of your CTA. Business looking to enhance its effectiveness produces a new version of ones CTA, or maybe moves that it to some other position on particular web page. Half ones traffic the internet site appeals to looks given the newest CTA, as the last half views the initial CTA as it had been. [[https://www.seoagencyleicester.com/ seo agency leicester]] After a given period of time, the internet site holder studies the data built-up by means of analytics. They see that as the original CTA attracted clicks at 15 associated with utter traffic, the newest CTA triggered your click-thru speed to 20. This could consequently claim that the newest CTA looks much more effective and certainly will consequently be manufactured your permanent function associated with website. The interational break most likely arrived during the perfect occasion for the Reds because they hoped doing their national obligations. Countless Liverpool players are dynamic upon around the world obligation prior this week plus some of them have now been to a good level noteworthy because of their divide nations because they hoped make it possible for their side to generally meet each demands for the up and coming 2018 FIFA worldwide Cup.Having wherein this deluxe retail stores select the worldwide to help expand their private brand equity is in fact initial your chatting they normally use about their your web visitors. Correct up until recently, almost all enterprises so that you can accept in addition to the girl devotees upon distinct web pages. Developed expanded primarily now that Hub web pages engineered two-way calls crucial, a number of deluxe stores best-known what this is quite a wonderful method for them to much more their priceless .In concept, split testing could be used to increase the effectiveness of anything on a site. But, there are particular properties as well as fixtures more commonly the subject of split testing, including.The answer the best campaign to strategic split up testing.Its crucial that you know exactly how all of the information on earth looks to zero authentic worth, in the event that you don't understand what to do with it. Or perhaps that may be the case among hundreds, decide not to behave on it. Nonetheless, don't make use of the information a person collect and you also could possibly be selling your business quick.Specifically Was Split Testing?inside easiest terms, separate assessment try an activity that requires comparing possible modifications towards web business like-for-like. A couple another models of your website are manufactured the one that gifts ones modifications you're considering, another staying unchanged. Customer base are definitely channelled to simultaneously models of your website, alongside analytics enabling you to observe perhaps the proposed changes has got the desired effect.Used strategically, separate assessment try a way of earning positive each and every modification means one noticeable improvement. Their an incident of capitalising regarding the simpleness of learning from mistakes, the use of rough evidence to steer ones decisions you attain. In addition, separate assessment can be handy towards preventing errors inside judgement that may prove counterproductive or high priced. In theory, split up screening could be used to increase the effectiveness out of anything regarding any sort of websites. But, there are particular qualities as well as fixtures more commonly subjected to split up screening, such as.


Who Are You Looking For In Odum, Georgia?
Run a search by name for anyone in Odum, Georgia & get free white pages information instantly. Odum, Georgia white page directory listings include full name, phone number and address.


John Beer will read from the book he edited: Robert Lax, Poems (1962-1997) (Wave Books, 2013). John is the author of The Waste Land and Other Poems (Canarium, 2010), which won the Norma Farber First Book Award from the Poetry Society of America. A graduate of the Iowa Writers' Workshop, he lived on the Greek island of Patmos for two years in the late 1990s, where he served as literary assistant to Robert Lax. He currently teaches creative writing at Portland State University; previously, he reviewed theater for Time Out Chicago.


At invotrak we design with simplicity and useability in mind. We believe that the best products are easy to use and completely intuitive.
We created invotrak to help with our own invoice-tracking needs, and found it may be helpful to others as well.
Free plans are granted 2 clients, 1 employee account, and 2 invoices a month so that you can fully test out everything that invotrak has to offer without any risk. You can upgrade (and downgrade) your account at any time.
If you have greater needs, upgrade to a paid plan and get unlimited clients and employee accounts, and a greater number of invoices you can send.
3 different tiers of paid plans allow you to find the right balance for your invoicing needs without overpaying.
Create and track invoices you issue to clients.
Determine what you're owed, by whom, and when it's due.
Keep track of timesheets for yourself and your employees.
Notify your clients of new invoices.
Save time & collect your money.
Are you invotrak? Visit our How To Get Listed On SmallBusinessExpo.com page to learn how you can customize this page FREE.


text:
HomePeepsJohn Rowe, Last Principal at Arlington High School Through 'Class to the Last' Dies at Age 92
John Rowe, Last Principal at Arlington High School Through 'Class to the Last' Dies at Age 92
Mar 15, 2019 05:33 pm Cardinal News Peeps Comments Off on John Rowe, Last Principal at Arlington High School Through 'Class to the Last' Dies at Age 92
Retired Arlington High School Principal John Rowe in 2017.
John Rowe, the last high school principal to serve the students and parents at Arlington High School, died Thursday March 14, 2019 at 7:00 p.m. He was a leader of positive thoughts when Township High School District 214's need to close a high school split the community from 1981 to 1984. Arlington High School was the first District 214 high school to open in 1922, and it was the first high school to close in 1984.
John Rowe began working for Township High School District 214 in 1956 as a high school math teacher. Initially, he only worked at Arlington High School for one year, and then was assigned to Prospect High School when it opened. He returned to Arlington, starting the '61-'62 school year and was soon promoted to Assistant Principal when Assistant Principal Bruno Waara was promoted to Principal. When Waara retired in 1980, John Rowe was promoted to Principal, continuing through to the 'Class to the Last' closing of Arlington High School in 1984.
Rowe was a highly respected and unassuming man, who was a natural leader and motivator for excellence from students. Funeral arrangements have not been announced, but services will be held in Arkansas, where John Rowe lived in retirement — and where he said, "I think of Arlington High School almost every day."
John Rowe in 1971 and at Homecoming 2002.
John Rowe – Final Arlington Speech
A heartfelt goodbye to Mr. Walter John Rowe, a longtime District 214 educator and Arlington High School's final principal. He passed away tonight.Principal Rowe started in District 214 as a math teacher at Arlington in 1956 and the following year, when Prospect High School opened, he was asked to teach math at Prospect.He stayed at Prospect until 1962, when he secured the position of Assistant Principal at Arlington High School. He took over as principal in 1980.When the school closed in 1984, Principal Rowe delivered an impassioned speech to his beloved Arlington Cardinals, using the phrase "Class to the Last" — words that also hung on a large banner and were captured in many photographs. Rest in peace Principal Rowe, and thank you for your tireless dedication.Listen to some of his final comments to Arlington grads.
Posted by High School District 214 on Thursday, March 14, 2019
Video from "Lady in Red" produced by Brian F. Sullivan, Class of 1984 with words from Principal John Rowe about Arlington High School closing in 1984.
^^ MOBILE? USE VOICE MIC ^^
GET ALERTS on Facebook.com/ArlingtonCardinal
GET ALERTS on Facebook.com/CardinalEmergencies
GET ALERTS on Facebook.com/ArlingtonHeightsCrime
Get updates from The Cardinal ALL NEWS FEEDS on Facebook. Just 'LIKE' the 'Arlington Cardinal Page (become a fan of our page). The updates cover all posts and sub-category posts from The Cardinal — Arlingtoncardinal.com. You can also limit feeds to specific categories. See all of The Cardinal Facebook fan pages at Arlingtoncardinal.com/about/facebook …
Help fund The Cardinal Arlingtoncardinal.com/sponsor
high school principal
Hyperlocal A
Hyperlocal B
Township High School District 214
Automatic Sprinkler Stop Fire in 5th Floor Condo on Smith Street Near Colfax St, Palatine
Lakemoor Couple Arrested After Illegal Weapons Found with a Falsified FOID Card in Home

redpajama_set_name: RedPajamaCommonCrawl


Dinosaur hunter is the typical survival game in multiple types of open world locations against prehistoric creatures. Be a real deadly dinosaur hunter in best of dinosaur hunting games 2019. Dino hunter on the deadly shores hunting is a real thrill and fun game of enjoyment if you are a real hunter then go and pick assault rifle of your choice and start hunting. T-rex dinosaurs are dangerous and attack you and you have to kill & shoot them as soon as possible. Various Jurassic beast models like carnotaurus, tyrannosaur rex, stegosaurus, diplodocus, raptor, spinosaurus and triceratops in dino hunting game as safari sniper. Get ultimate sniper hunting experience and accept the hunting challenges in prehistoric safari lands.
In dinosaur hunting games for free choose x-ray gun for direct head & body shots in cold snow weather. Try this hunting simulator 2019 which is typical FPS dinosaur hunt simulation game. This dino hunting game is best for dino hunters in jungle of carnivores dinos, you will use guns and snipers. As best sniper dino shooters of free dino games you need to hit with accuracy. Perform deadly hunt in thus hunter simulator game of hunting challenges. Dinosaur Hunter 2019 is the latest thrilling shooting game where you have to play the role of a stealth huntsman in dinosaur shooting games. Attack & Shoot giganotosaurus, herbivorous, triceratops & Apatosaurus. Hunt and gather your weapons and try to pass all survival missions in jurassic hunting simulation game.
Be careful while wild dinosaur & t rex are dangerous and can attack after missing the shot. For best shooting results use the scope with dinosaur hunter survival games. Explore sniper mode and assault mode with variety of modern sniping rifles & guns where you will be able to move around and attack from all around. Kill the most ferocious animals in history and enjoy the shooting of dinosaur in survival game. In this exciting dinosaur game only one winner, you or your enemies - ruthless bloodthirsty dinosaurs. Hit and smash these horror villain lizards and flying raptors. Show your brutal attacking skills to hunt down wild beast in the jungle and save yourself from their counter attacks in this survival islands.
✓ Discover hunting worlds like Jurassic rainforest, snow jungle & desert.
✓ Easy to use FPS rifles, guns & special X-ray shot gun.
✓ Ultimate crazy safari combat battle with UI interface.
✓ 3D dinosaurs models from the herbivorous to deadly carnivorous.
✓ Dynamic shadows, high resolution textures in dinosaur shooting games.
✓ Destructive weapons & powerful arsenal range.


text:
Best Chromebook for Students 2021
Ara Wagoner
Source: Ara Wagoner / Android Central
Best Chromebook for Students Android Central 2021
Chromebooks are great computers for a lot of different uses, but the best Chromebook for students single out a few particular features in addition to an easy-to-use computing experience. Education Chromebooks are designed to withstand the use and abuse of a classroom full of kids, as well as being carried around in an overstuffed backpack. Reinforced hinges and ports are the norm, as are water-resistant keyboards and all-day battery life. These lightweight laptops also have some of the easiest and most useful keyboard shortcuts to help your child get homework done quickly once they find their rhythm. There are a lot of great options to choose from, but the best Chromebook for students overall is the ASUS Chromebook Flip C214.
Best Chromebook for Students: ASUS Chromebook Flip C214
Best Ports and Durability: Dell Chromebook 3100 2-in-1
Best Non-Kidproofed: Acer Chromebook Spin 311 (CP311-3H)
8GB RAM Upgrade: HP Chromebook x360 11 G3 EE
Best for High School: Lenovo Chromebook Flex 5
Upgrade Pick: Acer Chromebook Spin 713
Best Chromebook Tablet: Lenovo Chromebook Duet
Best on a Budget: Lenovo Chromebook Flex 3
The ASUS Chromebook Flip C214 is the best Chromebook for students because it provides an excellent experience in a compact, durable package. The 11.6-inch screen is easy to see in a wide array of angles — even in the Florida sunshine — and the zinc alloy hinge allows you to rotate the screen around 360 degrees into tent or tablet mode as needed. A 2-in-1 Chromebook allows your student to interact with lessons and content however they want: with the keyboard and mouse or directly with the touchscreen.
There's also the option for a C214 with a built-in stylus, which is a great inclusion for those who have problems touching smaller touch targets accurately or those who like to doodle in tablet mode. I've adored the usefulness of a stylus for precision tapping in tent/tablet mode and when I'm reading webcomics while eating powdery snacks, which is what I use it for most often.
I have adored the usefulness of a stylus for precision tapping and to keep my screen clean while I'm eating powdery snacks.
The C214 lasts about 10 hours on a single charge, meaning it should last the whole school day and some homework before you need to seek out a charger. Like most modern Chromebooks, the C214 uses a 45W Power Delivery charger, meaning even if your kid loses or breaks the in-box charger, you can find a replacement charger easily and affordably.
There are also USB-C ports on each side of the C214, along with one USB-A port and a microSD slot to help expand the 32GB of storage the C214 comes with. The microSD slot on the side of this model is recessed, helping avoid accidental ejection, but the edges are sculpted so that a clothespin edge is the perfect size for inserting and ejecting cards. As a nail-biter, this small joy speaks to the attention to detail ASUS has taken with the rugged, reliable C214.
Yeah, there's only 32GB of internal storage here and only 4GB of RAM, but 4GB of RAM is enough for schoolwork, research, and Twitch streams — I mean homework!! 32GB of storage could get cramped when installing tons of Android apps, but for most elementary and middle-schoolers, it should be more than enough. The corners of the Chromebook have rubberized edges and the outer shell is covered with non-slip textures that are scratch-resistant. The keyboard is spill-resistant and the ports are reinforced, too, so hopefully, it should be able to withstand a few years of chaos with your child.
Exemplary battery life
Stylus is nice
Screen is readable in full sun
Compact and rugged
Spill-resistant keyboard
Only 4GB RAM
Popular and sells out frequently
Best Chromebook for Students
Rugged, reliable, and ready to pull an all-nighter
With a durable shell and easy-to-read touchscreen, the C214 is a Chromebook great for the classroom and homework.
$382 at Amazon (Non-Stylus)
$421 at Amazon (Stylus)
Dell is a manufacturer long-known for making school-tailored computers — my schools' computer labs and libraries were filled with Dells back in the day — and the Dell Chromebook 3100 2-in-1 is one of the most popular and durable Chromebooks on the education market today, which is part of why they're so gosh-darned popular with schools that they've been severely back-ordered for most of 2020.
The 3100 2-in-1 has two USB-A and USB-C ports — one on each side — as well as a microSD slot and headphone jack. While the extra USB-A port isn't necessary, spares are always appreciated when they can fit in basically the same size chassis. There's no stylus option here, which is a bummer, but performance is decent and there is an 8GB RAM configuration that I highly recommend getting if your kid has been having issues with Meet or Zoom crashing on their old, school-issued laptop. There was an option for 64GB of storage, too, but that configuration seems to have vanished, which is a bummer.
While you can indeed save a few dollars opting for the clamshell version, I recommend most users get a 2-in-1 Chromebook and for studious children, a 2-in-1 is darn near required. Tablet mode is great for using away from classroom desks or in the car, and tent mode turns the 3100 into a digital art easel. It's absolutely worth the extra cash.
However, there's an Achilles' heel with the Dell 3100 line: you can't get it immediately. Dell will let you put in a purchase order and get on the list, but delivery dates for both the 2-in-1 and clamshell models are slated for February right now, meaning that your child would be without it for a couple of months.
MIL-spec durability and spill resistance
Plenty of USB-A and C ports
8GB RAM option
Chrome updates until June 2025
No delivery dates for months
No stylus
Best Ports and Durability
Dell Chromebook 3100 2-in-1
Made for the real world, but priced above the classroom
While you can get higher memory/storage than the C214, the base model starts at a higher price, but you'll get a quality device.
From $369 at Dell
Acer's had several generations of the CP311 over the last several years, and the latest one is a great option for parents that want their child to have a Chromebook with a long support life ahead of it and enough power for distance learning — or for full-time adult work, as I worked full-time on this Chromebook for a month. This Mediatek-powered Chromebook can handle a Google Meet call and 6-10 tabs at a time, so it's perfectly suited to students that know how to take care of a laptop.
This isn't as rugged or rough-and-tumble as the ASUS C214 or Dell 3100, but that also means you don't have to pay for all that extra kid-proofing and durability testing. While most Chromebooks have a decent level of durability, there's no replacement for buying an Education model, so be sure your kid won't do anything like throw their laptop or stick crayons in the USB-C port.
That last bit is especially important because unlike most Chromebooks from the last 2-3 years, the Spin 311 (3H) only has one USB-C port and one USB-A port. I usually prefer Chromebooks with USB-C ports on each side of the laptop so that you can charge from either side, but it's easy enough to get by on just one. There's also no microSD card slot, so you'll need to figure out the storage level you'll need before buying since there's no expanding it.
For this reason, I highly recommend grabbing the 64GB option, especially since few 11.6-inch Chromebooks have 64GB options. Both the Dell and ASUS lack a 64GB option right now, and more internal storage is uber-important when it comes to using Android apps, as most can't properly utilize external storage due to quirks in Chrome OS's permission structure.
Lightweight, thin design
64GB storage option
Chrome OS updates until June 2028
Not ruggedized
Limited ports / no microSD
Best Non-Kidproofed
Acer Chromebook Spin 311 (CP311-3H)
If you trust your kid, this is all you need.
If your student is ready for a "big kid" Chromebook, the Acer Spin 311 is perfect for long days of homework and distance learning.
$329 at Walmart (64GB)
$300 at Target (32GB)
Source: HP
Stop me if you've heard this one: you're in the middle of a video conference, you go to pull open an email or some notes, and then the audio suddenly cuts out. You pop back to the video call only to find it's crashed — or worse, the whole dang browser's crashed. Rushing to reload the windows and rejoin the meeting, you miss vital information or hold up the class.
That happened because you ran out of RAM.
See, RAM is the temporary storage that your Chromebook uses to hold your current tabs and do whatever operations you're undertaking, be it scrolling through Twitter or trying to finish that digital test before time runs out. And when you go to do something new when the RAM is full, it dumps some of the older activities to carry them out — or it tries to dump the old activity, fails, and crashes. This is why RAM is important, and while 8GB Chromebooks are becoming more common for bigger, beefier business models, they're still relatively rare in the 11.6-inch segment, which is why I'm grateful to HP for offering an 8GB model of the HP Chromebook x360 11 G3 EE.
The x360 11 G3 EE (EE is the Education Edition) is as durable as the ASUS C214 and Dell 3100, and it has the same ample selection of ports and the same 11.6-inch 1366x768 pixel touchscreen. What it has over the ASUS and the Dell is that it has an 8GB model that's not back-ordered from here to Spring Break, and it also packs 64GB of internal storage, which is important if you or your kid will be downloading a lot of apps or offline content to watch during long trips or lazy Sunday afternoons.
8GB RAM for more tabs
Compact, durable design
Plenty of ports for peripherals
Hard to find 8GB model
The name is ridiculous
8GB RAM Upgrade
For long days of video calls and multi-tasking.
If your kid is fed up with their video call crashing while they were pulling up notes or assignments during class, this is the perfect solution.
$399 at HP
$399 at B&H
Best for High School: Lenovo Flex 5 Chromebook
The Lenovo Flex 5 Chromebook is the best value in Chromebooks today because it offers up just about all the features you'd want in a mid-size Chromebook without costing a whole semester's worth of allowance. This laptop is physically less than an inch bigger than the ASUS C214, but it has a much larger 13.3-inch 1080p touchscreen, a backlit keyboard, and nice, loudspeakers. You also get a more powerful Intel Core i3 processor instead of a Celeron and you get more internal storage for running Linux apps or downloading Disney+ movies for when you're bored in study hall.
To quote Han Solo, "she's got it where it counts, kid".
This laptop hasn't been ruggedized and life-proofed like the C214, but the Lenovo Flex 5 is as perfect for an older kid as it is for many adults. You get a comfortable, evenly-backlit keyboard for late-night procrastinated projects and a pair of big, front-facing speakers on either side to keep the tunes going while you work. The port configuration here is a match to the C214 — two USB-C ports, one USB-A port, a microSD slot, and a headphone jack — but you'll need to be more gentle on this laptop if you want it to last you its full eight years of Chrome OS updates.
The battery here should last you a whole school day and homework, and charging it back up shouldn't take too long with the in-box 45W PD charger. Not many families have Wi-Fi 6 routers — and even fewer schools do — but you should still get better Wi-Fi stability with it, and the same can be said of Bluetooth 5.0 for your headphones or Bluetooth mouse.
If there's a downside to the Flex 5 besides it not being kid-proofed, it's that it's a brand-spanking-new model and has been very hard to find in stock. If you can find it, your high schooler will thank you for getting them one of the best Chromebooks for students and adults alike.
Crisp 1080p touchscreen
Loud upward-facing speakers
Great internal specs
Wi-Fi 6 compatibility
Not as durable
Best for High School
Lenovo Chromebook Flex 5
Everything a teen could want
The best Chromebook for everyone is also a great model for middle or highschoolers who don't need a kid-proofed laptop.
If you absolutely need all the power you can get in a Chromebook but don't have buckets of money to spend on a new laptop, turn your attention to the Acer Chromebook Spin 713. This Project Athena Chromebook has a 10th Gen Intel Core i5 processor — powerful enough to run Windows programs via Parallels whenever the program comes from Chrome Enterprise to Chrome Education — 8GB of RAM so that you can multitask all the live-long day, and a 128GB SSD. That SSD is important because it's faster and longer-lasting than the eMMC storage the majority of Chromebooks use.
Another benefit of the Spin 713's powerful internals is that whenever Steam gaming properly comes to Chromebooks, the Spin 713 is beefy enough to handle it, let alone any game streaming services like Stadia, GeForce Now, or Xbox Game Pass. This power also means that the Spin 713 can handle just about any Linux app you throw at it, which is good news for STEM kids who might need to run CAD software or a programming IDE — there's a whole lot of IT professionals and programmers who use a Chromebook because they're low maintenance but can still do everything they need.
Another benefit of the Spin 713 is that unlike the other early Project Athena Chromebooks, this Chromebook packs a USB-A port and an HDMI port in addition to the two USB-C ports and the microSD slot.
Then, of course, there's the feature that you'll adore every single day whether you're working hard or hardly working: the 13.5-inch touchscreen, which gets to a bright 450 nits — so you can use it outside more easily — and features a 2K resolution, so videos and classroom handouts alike will look absolutely stunning. The screen is a 3:2 aspect ratio, which means you'll see further down a webpage at one time, especially when split-screening your research paper and that Twitch stream— I mean TED Talk, boring, educational TED Talk.
Powerful futureproof specs
Bright 3:2 screen for productivity
HDMI port built-in
Speakers aren't great
Acer Chromebook Spin 713
For power-users and would-be gamers
If you're someone who loves a 3:2 screen and a beefy, beefy processor to power it, this Project Athena Chromebook is for you.
From $669 at Amazon
While I'd love to direct you towards a kid-proofed Chrome OS tablet — because they're about half a dozen versions of one out there — I can't in good conscience do it. The education-grade Chrome OS tablets on the market today are either two years old, severely overpriced because of 2020's lockdown-fueled Chromebook shortage, or they're just not sold in commercial channels.
The only Chrome OS tablet worth recommending isn't a kid-proofed model, but it's a very, very good Chrome OS tablet with a very crisp 1080p screen, at least 64GB of storage, and 8 years of Chrome OS support ahead of it. Oh, it also comes with a detachable kickstand and detachable keyboard, so you don't have to go spend an extra hundred bucks on accessories straight out the gate like you would with an iPad.
The Lenovo Chromebook Duet has a battery that goes on and on and on, especially if you're just doing some reading and "edutainment" games, and the screen is nice and bright for watching videos and reading comics. The specs say the battery lasts 10 hours, but I've averaged 10-12 hours on an average charge.
With a 10.1-inch screen, it's little wonder that the included keyboard can feel a bit cramped to adults, but the keyboard is a great size for kids, and since it's a pogo connector rather than Bluetooth, you don't have to worry about charging the keyboard or spotty connections. And it's easy to detach when you're done with homework and ready to veg out with some e-comics for a few hours. I've lost whole weekends reading fanfiction on the Duet, and I do not regret a single second of it.
There's only one port on the Duet, one single USB-C port, so you'll want to use Bluetooth headphones and leave the port free for charging. There's also no microSD slot, but 64GB should be enough for most kids and there's a 128GB model available. I'm slightly disappointed there isn't a USB-C port on both sides the way that basically every Chromebook released in the last two years has, but there had to be a compromise somewhere.
It's also worth noting that the Duet comes with a 10W USB-A charger that you'll want to upgrade to an 18W Power Delivery charger for faster recharges — unlike regular Chromebooks, the Duet does not charge at 45W, though since it's Power Delivery, you can plug it into a 45W charger and it'll still charge at 18W.
Excellent battery life
Kickstand and keyboard included
Bright 1080p screen for comics and videos
Too small for some
Only one USB-C port
Best Chromebook Tablet
Lenovo Chromebook Duet
A great tablet for kids and adults
The Duet is the Google tablet we've wanted for a decade. It comes with a keyboard, but it works well all on its own.
From $259 at Amazon (64GB)
$299 at Best Buy (128GB)
A Chromebook that held a place of honor in both this roundup and the Best Chromebooks roundup for much of 2019 and into the first half of 2020 was the Lenovo Chromebook C330, which was my beloved run-and-gun that I used all over Walt Disney World on sunny afternoons and deep into the evenings as I waited for fireworks. The battery lasted all day, there were just enough ports for everything I needed, and it was lightweight enough that it wasn't a chore to carry it around all day in my gear bag. It was a great Chromebook that is gett price gouged thanks to the Chromebook shortage, but we can get that same sweet experience at a lower price in the Lenovo Chromebook Flex 3.
How do I know the Flex 3 is the same great Chromebook guts as the C330 in a newer, harder-to-blemish Platinum Grey shell? The ports line up exactly — even the full-size SD card slot (newer platforms use microSD) — we're dealing with the same MediaTek processor and 4GB of RAM. While that does mean this Chromebook is a tiny bit older, it still runs Chrome OS excellently and is a perfect Chromebook for parents on a budget who now need laptops for every kid in the house.
The full-size SD card and the single USB-C port aren't as good as having two USB-C ports and a smaller microSD slot — microSD slots are easier to keep a card in all the time without them getting jostled or broken — but this is a dependable little laptop for those that need a good experience at an even better price. You still get solid performance, you get a 2-in-1 touchscreen form factor, and you get all-day battery life for hundreds less than education-oriented models like the ASUS C214 and Dell 3100.
The Flex 3 also inherited a propensity for sales from the Lenovo C330, and Best Buy has discounted it several times over the last few months.
Affordable and durable
Touchscreen 2-in-1
Dependable keyboard
Older specs
No higher storage/RAM options
Shorter Chrome OS update life
Best on a Budget
New name, same great performance
The Lenovo Chromebook Flex 3 might be a rehash, but it's a remake of a good laptop and it'll serve your kid well for years to come.
What makes the best Chromebook for students?
When picking a Chromebook for a student, you may be tempted to go with a non-touch model, but fight this urge! Touchscreen Chromebooks aren't much more expensive these days, and they are far easier to interact with, especially for students that may be playing educational games or using artistic apps to doodle away their boredom. Styluses like the one on the ASUS Chromebook Flip C214 are great for art apps — and for people who tend to fat-finger their touch targets as I do — but it's alright to skip it if you don't think you need the extra (easy to lose) piece.
The C214 can be a little hard to come by right now — honestly, most good Chromebooks are hard to find right now without price gouging — and if you can't find the C214, I highly recommend the compact, 8GB RAM-packed HP Chromebook x360 11 G3 EE or the larger and slightly more powerful Lenovo Flex 5 for older students. Between school districts clamoring for more Chromebooks and parents scrambling for laptops for their kids, 2020 has made Chromebooks a hot commodity, especially the education-oriented Chromebooks built to endure classroom abuse.
11.6 inches is the standard size for Chromebooks — education and regular — because it's small enough to be easily portable while still being big enough to get your work done on. While 13-14 inch laptops make sense for teachers that stay in one room most of the day, for students lugging laptops between periods, smaller and lighter is better. Quality 14-inch touchscreen Chromebooks will also strain your wallet, but if you want something larger than a standard spiral notebook, head on over to our best Chromebook guide for those recommendations.
Why use a Chromebook for students in the classroom?
There are several reasons so many schools use Chromebooks in the classroom, and each one is just as important as the next.
Powered by Google — Google is a household name when it comes to technology and school districts know the company will be around to offer support for the life of the product.
Ease of use — Chrome OS is a lightweight system that's easy to find your way around, even for younger users and parents that aren't tech-savvy.
Security and administration tools — Chrome OS is designed with security at the forefront (since Chromebooks are also aimed at the Enterprise space), and a G Suite administrator can lock things down to meet the needs of a school system's IT policies.
Price — Chromebooks designed for classroom use can be bought in bulk for hundreds of dollars less than other devices like iPads and Windows laptops.
Parents can provide a Chromebook, too Because they are inexpensive and easy to use, parents can provide a Chromebook for a child who isn't in a 1:1 school program or during the summer recess.
School systems are notoriously cash-strapped and understaffed, yet they have the responsibility of shaping the next generation through their formative years and beyond. School officials have told me that this can be the most frustrating part of their job because sometimes they can't provide teachers and other hands-on educators with the tools they need to teach our children. Because Chromebooks are inexpensive to buy and support, they are a welcome option for frustrated, underfunded school districts.
In the classroom itself, Chromebooks provide a gateway to everything a student needs to learn, and everything a teacher needs to guide them. Little things a consumer may take for granted, like automatic updates and the ability to sign in to any device and have your profile available mean more time can be dedicated to studies instead of administration.
Chromebooks are designed to be used with Google's educational suite of applications.
Chromebooks also work seamlessly with Google's educational software: Google Classroom, G Suite for Education, and even Google's consumer applications like Gmail or Google Keep. With these apps and services, students and teachers can work online or offline, seamlessly syncing with Google's servers.
It's very difficult to tell you're working with an app that stores its data in the cloud because the experience is so good, but you'll know it did when you pick up a completely different Chromebook and everything is just as you left it. This is great for students, allowing them to get back to work after they spill water all over their Chromebook and have to be issued a new one.
Chromebooks and Google's educational application suite are simple to use, well integrated into inexpensive Chromebooks, and are the perfect foundation for education.
What's special about a Chromebook for Education?
You'll see a lot of Chromebooks out there branded as being Chromebooks for Education, and you may be wondering what exactly the difference is between an Education Chromebook and a regular one.
Really, to normal buyers, there isn't one, at least from a hardware standpoint.
Any Chromebook can be used in the classroom and still have access to Google's educational suite and work, communicate and collaborate with other students and teachers, just as soon as you log into it. What is different is the support contract. School districts that purchase through education channels can also contract various levels of support from the company that made the Chromebooks or through Google itself, such as on-site service and having a Google specialist help students and teachers get started in person.
Education Chromebooks all have one thing in common: they're tough.
Education-focused Chromebook models are often built with an eye on durability. You'll find thick polycarbonate shells and rubber bumpers to withstand bumps and falls, MIL-SPEC approval for survivability in the elements, and even special features like microbe-resistant screens to cut down on spreading germs from student to student or waterproof keyboard pans — kids are messy, after all.
Google does have some standards a Chromebook must adhere to for the educational certification, but they are common-sense requirements that almost all Chromebooks follow. Ever wonder why a Chromebook has lower-case letters on the keyboard instead of capital letters like most other keyboards? That's part of Google's requirements. For a small child learning how to read and type, a key must be marked with what it will print on the screen when pressed. Ingenious!
Credits — The team that worked on this guide
Ara Wagoner themes phones and pokes YouTube Music with a stick. When she's not writing help and how-to's, she's running around Walt Disney World with a Chromebook. You can follow her on Twitter at @arawagco. If you see her without headphones, RUN.
Jerry Hildenbrand is Mobile Nation's Senior Editor and works from a Chromebook full time. Currently, he is using Google's Pixelbook but is always looking at new products and may have any Chromebook in his hands at any time. You'll find him across the Mobile Nations network and you can hit him up on Twitter if you want to say hey.
a plug for everything
Make your Chromebook more powerful with these docking stations
If you need to turn your Chromebook into an easy-to-use desktop once in a while, these docking stations are exactly what you're looking for.
Game Anywhere
Get an upper hand on the competition with the best Chromebook controllers
While there has been some type of gaming capability on Chromebooks, it didn't live up to expectations for a while. However, updates have been regularly released, which has opened the door for a better gaming experience to be had on Chromebooks.
These are the best external hard drives for your Chromebook
Expanding the available storage space on your Chromebook is really easy. All you need is an external hard drive or solid-state drive (SSD) that can use one of the available USB ports. Here are the best options for your Google-powered laptop.


text:
Automate your social media reporting
Profile Analytics
Track and report on the Pages and Profiles you admin
Ads Analytics
Track and report on the Ad Accounts you admin
Competitor Analytics
Track and report on any public competitor Pages and Profiles
Influencer Analytics
Track and report on your Influencer Campaigns
START FREE Login
Social Media Best Practice
Social Media Benchmarks
Snapchat Push to Remain Platform of Choice by Introducing Campus Publisher Stories
By Teagan Harbridge
Thursday September 21 2017
With 45% of Snapchat's user base between the ages of 18-24 (prime college age), Snapchat is leveraging off their dominance with younger audiences to introduce Campus Publisher Stories to their Discover Section.
A study conducted in partnership with MORI Research, indicated that college newspapers continue to reign as a top media choice among today's co-eds and a sought-after source for campus news, entertainment and shopping information [Alloy Media & Marketing].
With 76% of all college students reading their campus publication in the past 30 days, the move to include these campus stories is a strategic push for the platform to remain relevant and dominant for younger audiences.
Snapchat launched 'Discover' in early 2015 as a means to achieving their goal of becoming a "destination for consuming news".
Our ambition is very much that when something happens in the world or there's a news event, that our audience would turn to Snapchat increasingly to get that news" – Sean Mills, Head of Original Content.
Since then, Snapchat have launched a series of Stories including some of the biggest names in publishing from The New York Times, Fox Sports, National Geographic and Harpers Bazaar.
Snapchat Discover's facelift from 2015 to now – Image courtesy of TechCrunch
Snapchat have selected to partner with handful of colleges around the US including Berkeley, Texas A&M, Syracuse and The University of Wisconsin – Madison, to begin producing weekly Publisher Stories to be distributed on Snapchat.
The move sees Snapchat nurturing the "talent pool" stating that many of the leading journalists and editors get their starts working in school newspapers and they plan to help each school monetise and grow their newspaper through a revenue sharing agreement (although we are not quite sure what this entails).
Something (Snapchat) really stressed was to create content that made people feel like they were in on the joke or part of the campus community – University of Wisconsin – Madison, Editor in Chief – Alice Agun on her meetings with Snapchat
Snapchat still seems to have a fair share of control in the content being produced, working with students to develop the "right" kind of content, with a focus on virality and shareability.
The content will only be available to users within a close radius of the campus they are currently working with, aiming to create feelings of exclusivity and amplification of the community connectedness.
No word yet on when this will be rolled out to Australian Universities. We'll keep you posted.
Filed Under: News & Trends
Get Social Status Insights delivered to your inbox once a month:
Copyright © 2021. Social Status, Inc. All Rights Reserved. Contact sales +1-415-513-0679


text:
/**
 * @fileoverview This file contains various functions and classes for rendering
 * gpu based particles.
 *
 * TODO: Add 3d oriented particles.
 */

o3djs.provide('o3djs.particles');

o3djs.require('o3djs.math');

/**
 * A Module with various GPU particle functions and classes.
 * Note: GPU particles have the issue that they are not sorted per particle
 * but rather per emitter.
 * @namespace
 */
o3djs.particles = o3djs.particles || {};

/**
 * Enum for pre-made particle states.
 * @enum
 */
o3djs.particles.ParticleStateIds = {
   BLEND: 0,
   ADD: 1,
   BLEND_PREMULTIPLY: 2,
   BLEND_NO_ALPHA: 3,
   SUBTRACT: 4,
   INVERSE: 5};

/**
 * Particle Effect strings
 * @type {!Array.<{name: string, fxString: string}>}
 */
o3djs.particles.FX_STRINGS = [
  { name: 'particle3d', fxString: '' +
    'float4x4 worldViewProjection : WORLDVIEWPROJECTION;\n' +
    'float4x4 world : WORLD;\n' +
    'float3 worldVelocity;\n' +
    'float3 worldAcceleration;\n' +
    'float timeRange;\n' +
    'float time;\n' +
    'float timeOffset;\n' +
    'float frameDuration;\n' +
    'float numFrames;\n' +
    '\n' +
    '// We need to implement 1D!\n' +
    'sampler rampSampler;\n' +
    'sampler colorSampler;\n' +
    '\n' +
    'struct VertexShaderInput {\n' +
    '  float4 uvLifeTimeFrameStart : POSITION; // uv, lifeTime, frameStart\n' +
    '  float4 positionStartTime : TEXCOORD0;    // position.xyz, startTime\n' +
    '  float4 velocityStartSize : TEXCOORD1;   // velocity.xyz, startSize\n' +
    '  float4 accelerationEndSize : TEXCOORD2; // acceleration.xyz, endSize\n' +
    '  float4 spinStartSpinSpeed : TEXCOORD3;  // spinStart.x, spinSpeed.y\n' +
    '  float4 orientation : TEXCOORD4;  // orientation\n' +
    '  float4 colorMult : COLOR; //\n' +
    '};\n' +
    '\n' +
    'struct PixelShaderInput {\n' +
    '  float4 position : POSITION;\n' +
    '  float2 texcoord : TEXCOORD0;\n' +
    '  float1 percentLife : TEXCOORD1;\n' +
    '  float4 colorMult: TEXCOORD2;\n' +
    '};\n' +
    '\n' +
    'PixelShaderInput vertexShaderFunction(VertexShaderInput input) {\n' +
    '  PixelShaderInput output;\n' +
    '\n' +
    '  float2 uv = input.uvLifeTimeFrameStart.xy;\n' +
    '  float lifeTime = input.uvLifeTimeFrameStart.z;\n' +
    '  float frameStart = input.uvLifeTimeFrameStart.w;\n' +
    '  float3 position = input.positionStartTime.xyz;\n' +
    '  float startTime = input.positionStartTime.w;\n' +
    '  float3 velocity = mul(float4(input.velocityStartSize.xyz, 0),\n' +
    '                        world).xyz + worldVelocity;\n' +
    '  float startSize = input.velocityStartSize.w;\n' +
    '  float3 acceleration = mul(float4(input.accelerationEndSize.xyz, 0),\n' +
    '                            world).xyz + worldAcceleration;\n' +
    '  float endSize = input.accelerationEndSize.w;\n' +
    '  float spinStart = input.spinStartSpinSpeed.x;\n' +
    '  float spinSpeed = input.spinStartSpinSpeed.y;\n' +
    '\n' +
    '  float localTime = fmod((time - timeOffset - startTime), timeRange);\n' +
    '  float percentLife = localTime / lifeTime;\n' +
    '\n' +
    '  float frame = fmod(floor(localTime / frameDuration + frameStart),\n' +
    '                     numFrames);\n' +
    '  float uOffset = frame / numFrames;\n' +
    '  float u = uOffset + (uv.x + 0.5) * (1 / numFrames);\n' +
    '\n' +
    '  output.texcoord = float2(u, uv.y + 0.5);\n' +
    '  output.colorMult = input.colorMult;\n' +
    '\n' +
    '  float size = lerp(startSize, endSize, percentLife);\n' +
    '  size = (percentLife < 0 || percentLife > 1) ? 0 : size;\n' +
    '  float s = sin(spinStart + spinSpeed * localTime);\n' +
    '  float c = cos(spinStart + spinSpeed * localTime);\n' +
    '\n' +
    '  float4 rotatedPoint = float4((uv.x * c + uv.y * s) * size, 0,\n' +
    '                               (uv.x * s - uv.y * c) * size, 1);\n' +
    '  float3 center = velocity * localTime +\n' +
    '                  acceleration * localTime * localTime + \n' +
    '                  position;\n' +
    '  \n' +
    '      float4 q2 = input.orientation + input.orientation;\n' +
    '      float4 qx = input.orientation.xxxw * q2.xyzx;\n' +
    '      float4 qy = input.orientation.xyyw * q2.xyzy;\n' +
    '      float4 qz = input.orientation.xxzw * q2.xxzz;\n' +
    '  \n' +
    '      float4x4 localMatrix = float4x4(\n' +
    '        (1.0f - qy.y) - qz.z, \n' +
    '        qx.y + qz.w, \n' +
    '        qx.z - qy.w,\n' +
    '        0,\n' +
    '  \n' +
    '        qx.y - qz.w, \n' +
    '        (1.0f - qx.x) - qz.z, \n' +
    '        qy.z + qx.w,\n' +
    '        0,\n' +
    '  \n' +
    '        qx.z + qy.w, \n' +
    '        qy.z - qx.w, \n' +
    '        (1.0f - qx.x) - qy.y,\n' +
    '        0,\n' +
    '  \n' +
    '        center.x, center.y, center.z, 1);\n' +
    '  rotatedPoint = mul(rotatedPoint, localMatrix);\n' +
    '  output.position = mul(rotatedPoint, worldViewProjection);\n' +
    '  output.percentLife = percentLife;\n' +
    '  return output;\n' +
    '}\n' +
    '\n' +
    'float4 pixelShaderFunction(PixelShaderInput input): COLOR {\n' +
    '  float4 colorMult = tex2D(rampSampler, \n' +
    '                           float2(input.percentLife, 0.5)) *\n' +
    '                     input.colorMult;\n' +
    '  float4 color = tex2D(colorSampler, input.texcoord) * colorMult;\n' +
    '  return color;\n' +
    '}\n' +
    '\n' +
    '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' +
    '// #o3d PixelShaderEntryPoint pixelShaderFunction\n' +
    '// #o3d MatrixLoadOrder RowMajor\n'},
  { name: 'particle2d', fxString: '' +
    'float4x4 viewProjection : VIEWPROJECTION;\n' +
    'float4x4 world : WORLD;\n' +
    'float4x4 viewInverse : VIEWINVERSE;\n' +
    'float3 worldVelocity;\n' +
    'float3 worldAcceleration;\n' +
    'float timeRange;\n' +
    'float time;\n' +
    'float timeOffset;\n' +
    'float frameDuration;\n' +
    'float numFrames;\n' +
    '\n' +
    '// We need to implement 1D!\n' +
    'sampler rampSampler;\n' +
    'sampler colorSampler;\n' +
    '\n' +
    'struct VertexShaderInput {\n' +
    '  float4 uvLifeTimeFrameStart : POSITION; // uv, lifeTime, frameStart\n' +
    '  float4 positionStartTime : TEXCOORD0;    // position.xyz, startTime\n' +
    '  float4 velocityStartSize : TEXCOORD1;   // velocity.xyz, startSize\n' +
    '  float4 accelerationEndSize : TEXCOORD2; // acceleration.xyz, endSize\n' +
    '  float4 spinStartSpinSpeed : TEXCOORD3;  // spinStart.x, spinSpeed.y\n' +
    '  float4 colorMult : COLOR; //\n' +
    '};\n' +
    '\n' +
    'struct PixelShaderInput {\n' +
    '  float4 position : POSITION;\n' +
    '  float2 texcoord : TEXCOORD0;\n' +
    '  float1 percentLife : TEXCOORD1;\n' +
    '  float4 colorMult: TEXCOORD2;\n' +
    '};\n' +
    '\n' +
    'PixelShaderInput vertexShaderFunction(VertexShaderInput input) {\n' +
    '  PixelShaderInput output;\n' +
    '\n' +
    '  float2 uv = input.uvLifeTimeFrameStart.xy;\n' +
    '  float lifeTime = input.uvLifeTimeFrameStart.z;\n' +
    '  float frameStart = input.uvLifeTimeFrameStart.w;\n' +
    '  float3 position = input.positionStartTime.xyz;\n' +
    '  float startTime = input.positionStartTime.w;\n' +
    '  float3 velocity = mul(float4(input.velocityStartSize.xyz, 0),\n' +
    '                        world).xyz + worldVelocity;\n' +
    '  float startSize = input.velocityStartSize.w;\n' +
    '  float3 acceleration = mul(float4(input.accelerationEndSize.xyz, 0),\n' +
    '                            world).xyz + worldAcceleration;\n' +
    '  float endSize = input.accelerationEndSize.w;\n' +
    '  float spinStart = input.spinStartSpinSpeed.x;\n' +
    '  float spinSpeed = input.spinStartSpinSpeed.y;\n' +
    '\n' +
    '  float localTime = fmod((time - timeOffset - startTime), timeRange);\n' +
    '  float percentLife = localTime / lifeTime;\n' +
    '\n' +
    '  float frame = fmod(floor(localTime / frameDuration + frameStart),\n' +
    '                     numFrames);\n' +
    '  float uOffset = frame / numFrames;\n' +
    '  float u = uOffset + (uv.x + 0.5) * (1 / numFrames);\n' +
    '\n' +
    '  output.texcoord = float2(u, uv.y + 0.5);\n' +
    '  output.colorMult = input.colorMult;\n' +
    '\n' +
    '  float3 basisX = viewInverse[0].xyz;\n' +
    '  float3 basisZ = viewInverse[1].xyz;\n' +
    '\n' +
    '  float size = lerp(startSize, endSize, percentLife);\n' +
    '  size = (percentLife < 0 || percentLife > 1) ? 0 : size;\n' +
    '  float s = sin(spinStart + spinSpeed * localTime);\n' +
    '  float c = cos(spinStart + spinSpeed * localTime);\n' +
    '\n' +
    '  float2 rotatedPoint = float2(uv.x * c + uv.y * s, \n' +
    '                               -uv.x * s + uv.y * c);\n' +
    '  float3 localPosition = float3(basisX * rotatedPoint.x +\n' +
    '                                basisZ * rotatedPoint.y) * size +\n' +
    '                         velocity * localTime +\n' +
    '                         acceleration * localTime * localTime + \n' +
    '                         position;\n' +
    '\n' +
    '  output.position = mul(float4(localPosition + world[3].xyz, 1), \n' +
    '                        viewProjection);\n' +
    '  output.percentLife = percentLife;\n' +
    '  return output;\n' +
    '}\n' +
    '\n' +
    'float4 pixelShaderFunction(PixelShaderInput input): COLOR {\n' +
    '  float4 colorMult = tex2D(rampSampler, \n' +
    '                           float2(input.percentLife, 0.5)) *\n' +
    '                     input.colorMult;\n' +
    '  float4 color = tex2D(colorSampler, input.texcoord) * colorMult;\n' +
    '  return color;\n' +
    '}\n' +
    '\n' +
    '// #o3d VertexShaderEntryPoint vertexShaderFunction\n' +
    '// #o3d PixelShaderEntryPoint pixelShaderFunction\n' +
    '// #o3d MatrixLoadOrder RowMajor\n'}];

/**
 * Corner values.
 * @private
 * @type {!Array.<!Array.<number>>}
 */
o3djs.particles.CORNERS_ = [
      [-0.5, -0.5],
      [+0.5, -0.5],
      [+0.5, +0.5],
      [-0.5, +0.5]];

/**
 * Creates a particle system.
 * You only need one of these to run multiple emitters of different types
 * of particles.
 * @param {!o3d.Pack} pack The pack for the particle system to manage resources.
 * @param {!o3djs.rendergraph.viewInfo} viewInfo A viewInfo so the particle
 *     system can do the default setup. The only thing used from viewInfo
 *     is the zOrderedDrawList. If that is not where you want your particles,
 *     after you create the particleEmitter use
 *     particleEmitter.material.drawList = myDrawList to set it to something
 *     else.
 * @param {!o3d.ParamFloat} opt_clockParam A ParamFloat to be the default
 *     clock for emitters of this particle system.
 * @param {!function(): number} opt_randomFunction A function that returns
 *     a random number between 0.0 and 1.0. This allows you to pass in a
 *     pseudo random function if you need particles that are reproducable.
 * @return {!o3djs.particles.ParticleSystem} The created particle system.
 */
o3djs.particles.createParticleSystem = function(pack,
                                                viewInfo,
                                                opt_clockParam,
                                                opt_randomFunction) {
  return new o3djs.particles.ParticleSystem(pack,
                                            viewInfo,
                                            opt_clockParam,
                                            opt_randomFunction);
};

/**
 * An Object to manage Particles.
 * @constructor
 * @param {!o3d.Pack} pack The pack for the particle system to manage resources.
 * @param {!o3djs.rendergraph.ViewInfo} viewInfo A viewInfo so the particle
 *     system can do the default setup. The only thing used from viewInfo
 *     is the zOrderedDrawList. If that is not where you want your particles,
 *     after you create the particleEmitter use
 *     particleEmitter.material.drawList = myDrawList to set it to something
 *     else.
 * @param {!o3d.ParamFloat} opt_clockParam A ParamFloat to be the default
 *     clock for emitters of this particle system.
 */
o3djs.particles.ParticleSystem = function(pack,
                                          viewInfo,
                                          opt_clockParam,
                                          opt_randomFunction) {
  var o3d = o3djs.base.o3d;
  var particleStates = [];
  var effects = [];
  for (var ee = 0; ee < o3djs.particles.FX_STRINGS.length; ++ee) {
    var info = o3djs.particles.FX_STRINGS[ee];
    var effect = pack.createObject('Effect');
    effect.name = info.name;
    effect.loadFromFXString(info.fxString);
    effects.push(effect);
  }

  var stateInfos = {};
  stateInfos[o3djs.particles.ParticleStateIds.BLEND] = {
    'SourceBlendFunction':
        o3djs.base.o3d.State.BLENDFUNC_SOURCE_ALPHA,
    'DestinationBlendFunction':
        o3djs.base.o3d.State.BLENDFUNC_INVERSE_SOURCE_ALPHA};

  stateInfos[o3djs.particles.ParticleStateIds.ADD] = {
    'SourceBlendFunction':
        o3djs.base.o3d.State.BLENDFUNC_SOURCE_ALPHA,
    'DestinationBlendFunction':
        o3djs.base.o3d.State.BLENDFUNC_ONE};

  stateInfos[o3djs.particles.ParticleStateIds.BLEND_PREMULTIPLY] = {
    'SourceBlendFunction':
        o3djs.base.o3d.State.BLENDFUNC_ONE,
    'DestinationBlendFunction':
        o3djs.base.o3d.State.BLENDFUNC_INVERSE_SOURCE_ALPHA};

  stateInfos[o3djs.particles.ParticleStateIds.BLEND_NO_ALPHA] = {
    'SourceBlendFunction':
        o3djs.base.o3d.State.BLENDFUNC_SOURCE_COLOR,
    'DestinationBlendFunction':
        o3djs.base.o3d.State.BLENDFUNC_INVERSE_SOURCE_COLOR};

  stateInfos[o3djs.particles.ParticleStateIds.SUBTRACT] = {
    'SourceBlendFunction':
        o3djs.base.o3d.State.BLENDFUNC_SOURCE_ALPHA,
    'DestinationBlendFunction':
        o3djs.base.o3d.State.BLENDFUNC_INVERSE_SOURCE_ALPHA,
    'BlendEquation':
        o3djs.base.o3d.State.BLEND_REVERSE_SUBTRACT};

  stateInfos[o3djs.particles.ParticleStateIds.INVERSE] = {
    'SourceBlendFunction':
        o3djs.base.o3d.State.BLENDFUNC_INVERSE_DESTINATION_COLOR,
    'DestinationBlendFunction':
        o3djs.base.o3d.State.BLENDFUNC_INVERSE_SOURCE_COLOR};

  for (var key in o3djs.particles.ParticleStateIds) {
    var state = pack.createObject('State');
    var id = o3djs.particles.ParticleStateIds[key];
    particleStates[id] = state;
    state.getStateParam('ZWriteEnable').value = false;
    state.getStateParam('CullMode').value = o3d.State.CULL_NONE;

    var info = stateInfos[id];
    for (var stateName in info) {
      state.getStateParam(stateName).value = info[stateName];
    }
  }

  var colorTexture = pack.createTexture2D(8, 8, o3d.Texture.ARGB8, 1, false);
  var pixelBase = [0, 0.20, 0.70, 1, 0.70, 0.20, 0, 0];
  var pixels = [];
  for (var yy = 0; yy < 8; ++yy) {
    for (var xx = 0; xx < 8; ++xx) {
      var pixel = pixelBase[xx] * pixelBase[yy];
      pixels.push(pixel, pixel, pixel, pixel);
    }
  }
  colorTexture.set(0, pixels);
  var rampTexture = pack.createTexture2D(3, 1, o3d.Texture.ARGB8, 1, false);
  rampTexture.set(0, [1, 1, 1, 1,
                      1, 1, 1, 0.5,
                      1, 1, 1, 0]);

  if (!opt_clockParam) {
    this.counter_ = pack.createObject('SecondCounter');
    opt_clockParam = this.counter_.getParam('count');
  }

  this.randomFunction_ = opt_randomFunction || function() {
        return Math.random();
      };

  /**
   * The states for the various blend modes.
   * @type {!Array.<!o3d.State>}
   */
  this.particleStates = particleStates;

  /**
   * The default ParamFloat to use as the clock for emitters created by
   * this system.
   * @type {!o3d.ParamFloat}
   */
  this.clockParam = opt_clockParam;

  /**
   * The pack used to manage particle system resources.
   * @type {!o3d.Pack}
   */
  this.pack = pack;

  /**
   * The viewInfo that is used to get drawLists.
   * @type {!o3djs.rendergraph.ViewInfo}
   */
  this.viewInfo = viewInfo;

  /**
   * The effects for particles.
   * @type {!Array.<!o3d.Effect>}
   */
  this.effects = effects;

  /**
   * The default color texture for particles.
   * @type {!o3d.Texture2D}
   */
  this.defaultColorTexture = colorTexture;

  /**
   * The default ramp texture for particles.
   * @type {!o3d.Texture2D}
   */
  this.defaultRampTexture = rampTexture;
};

/**
 * A ParticleSpec specifies how to emit particles.
 *
 * NOTE: For all particle functions you can specific a ParticleSpec as a
 * Javascript object, only specifying the fields that you care about.
 *
 * <pre>
 * emitter.setParameters({
 *   numParticles: 40,
 *   lifeTime: 2,
 *   timeRange: 2,
 *   startSize: 50,
 *   endSize: 90,
 *   positionRange: [10, 10, 10],
 *   velocity:[0, 0, 60], velocityRange: [15, 15, 15],
 *   acceleration: [0, 0, -20],
 *   spinSpeedRange: 4}
 * );
 * </pre>
 *
 * Many of these parameters are in pairs. For paired paramters each particle
 * specfic value is set like this
 *
 * particle.field = value + Math.random() - 0.5 * valueRange * 2;
 *
 * or in English
 *
 * particle.field = value plus or minus valueRange.
 *
 * So for example, if you wanted a value from 10 to 20 you'd pass 15 for value
 * and 5 for valueRange because
 *
 * 15 + or - 5  = (10 to 20)
 *
 * @constructor
 */
o3djs.particles.ParticleSpec = function() {
  /**
   * The number of particles to emit.
   * @type {number}
   */
  this.numParticles = 1;

  /**
   * The number of frames in the particle texture.
   * @type {number}
   */
  this.numFrames = 1;

  /**
   * The frame duration at which to animate the particle texture in seconds per
   * frame.
   * @type {number}
   */
  this.frameDuration = 1;

  /**
   * The initial frame to display for a particular particle.
   * @type {number}
   */
  this.frameStart = 0;

  /**
   * The frame start range.
   * @type {number}
   */
  this.frameStartRange = 0;

  /**
   * The life time of the entire particle system.
   * To make a particle system be continuous set this to match the lifeTime.
   * @type {number}
   */
  this.timeRange = 99999999;

  /**
   * The startTime of a particle.
   * @type {number?}
   */
  this.startTime = null;
  // TODO: Describe what happens if this is not set. I still have some
  //     work to do there.

  /**
   * The lifeTime of a particle.
   * @type {number}
   */
  this.lifeTime = 1;

  /**
   * The lifeTime range.
   * @type {number}
   */
  this.lifeTimeRange = 0;

  /**
   * The starting size of a particle.
   * @type {number}
   */
  this.startSize = 1;

  /**
   * The starting size range.
   * @type {number}
   */
  this.startSizeRange = 0;

  /**
   * The ending size of a particle.
   * @type {number}
   */
  this.endSize = 1;

  /**
   * The ending size range.
   * @type {number}
   */
  this.endSizeRange = 0;

  /**
   * The starting position of a particle in local space.
   * @type {!o3djs.math.Vector3}
   */
  this.position = [0, 0, 0];

  /**
   * The starting position range.
   * @type {!o3djs.math.Vector3}
   */
  this.positionRange = [0, 0, 0];

  /**
   * The velocity of a paritcle in local space.
   * @type {!o3djs.math.Vector3}
   */
  this.velocity = [0, 0, 0];

  /**
   * The velocity range.
   * @type {!o3djs.math.Vector3}
   */
  this.velocityRange = [0, 0, 0];

  /**
   * The acceleration of a particle in local space.
   * @type {!o3djs.math.Vector3}
   */
  this.acceleration = [0, 0, 0];

  /**
   * The accleration range.
   * @type {!o3djs.math.Vector3}
   */
  this.accelerationRange = [0, 0, 0];

  /**
   * The starting spin value for a particle in radians.
   * @type {number}
   */
  this.spinStart = 0;

  /**
   * The spin start range.
   * @type {number}
   */
  this.spinStartRange = 0;

  /**
   * The spin speed of a particle in radians.
   * @type {number}
   */
  this.spinSpeed = 0;

  /**
   * The spin speed range.
   * @type {number}
   */
  this.spinSpeedRange = 0;

  /**
   * The color multiplier of a particle.
   * @type {!o3djs.math.Vector4}
   */
  this.colorMult = [1, 1, 1, 1];

  /**
   * The color multiplier range.
   * @type {!o3djs.math.Vector4} colorMultRange
   */
  this.colorMultRange = [0, 0, 0, 0];

  /**
   * The velocity of all paritcles in world space.
   * @type {!o3djs.math.Vector3}
   */
  this.worldVelocity = [0, 0, 0];

  /**
   * The acceleration of all paritcles in world space.
   * @type {!o3djs.math.Vector3}
   */
  this.worldAcceleration = [0, 0, 0];

  /**
   * Whether these particles are oriented in 2d or 3d. true = 2d, false = 3d.
   * @type {boolean}
   */
  this.billboard = true;

  /**
   * The orientation of a particle. This is only used if billboard is false.
   * @type {!o3djs.quaternions.Quaterinion}
   */
  this.orientation = [0, 0, 0, 1];
};

/**
 * Creates a particle emitter.
 * @param {!o3d.Texture} opt_texture The texture to use for the particles.
 *     If you don't supply a texture a default is provided.
 * @param {!o3d.ParamFloat} opt_clockParam A ParamFloat to be the clock for
 *     the emitter.
 * @return {!o3djs.particles.ParticleEmitter} The new emitter.
 */
o3djs.particles.ParticleSystem.prototype.createParticleEmitter =
    function(opt_texture, opt_clockParam) {
  return new o3djs.particles.ParticleEmitter(this, opt_texture, opt_clockParam);
};

/**
 * Creates a Trail particle emitter.
 * You can use this for jet exhaust, etc...
 * @param {!o3d.Transform} parent Transform to put emitter on.
 * @param {number} maxParticles Maximum number of particles to appear at once.
 * @param {!o3djs.particles.ParticleSpec} parameters The parameters used to
 *     generate particles.
 * @param {!o3d.Texture} opt_texture The texture to use for the particles.
 *     If you don't supply a texture a default is provided.
 * @param {!function(number, !o3djs.particles.ParticleSpec): void}
 *     opt_perParticleParamSetter A function that is called for each particle to
 *     allow it's parameters to be adjusted per particle. The number is the
 *     index of the particle being created, in other words, if numParticles is
 *     20 this value will be 0 to 19. The ParticleSpec is a spec for this
 *     particular particle. You can set any per particle value before returning.
 * @param {!o3d.ParamFloat} opt_clockParam A ParamFloat to be the clock for
 *     the emitter.
 * @return {!o3djs.particles.Trail} A Trail object.
 */
o3djs.particles.ParticleSystem.prototype.createTrail = function(
    parent,
    maxParticles,
    parameters,
    opt_texture,
    opt_perParticleParamSetter,
    opt_clockParam) {
  return new o3djs.particles.Trail(
      this,
      parent,
      maxParticles,
      parameters,
      opt_texture,
      opt_perParticleParamSetter,
      opt_clockParam);
};

/**
 * A ParticleEmitter
 * @constructor
 * @param {!o3djs.particles.ParticleSystem} particleSystem The particle system
 *     to manage this emitter.
 * @param {!o3d.Texture} opt_texture The texture to use for the particles.
 *     If you don't supply a texture a default is provided.
 * @param {!o3d.ParamFloat} opt_clockParam A ParamFloat to be the clock for
 *     the emitter.
 */
o3djs.particles.ParticleEmitter = function(particleSystem,
                                           opt_texture,
                                           opt_clockParam) {
  opt_clockParam = opt_clockParam || particleSystem.clockParam;

  var o3d = o3djs.base.o3d;
  var pack = particleSystem.pack;
  var viewInfo = particleSystem.viewInfo;
  var material = pack.createObject('Material');
  material.name = 'particles';
  material.drawList = viewInfo.zOrderedDrawList;
  material.effect = particleSystem.effects[1];
  particleSystem.effects[1].createUniformParameters(material);
  material.getParam('time').bind(opt_clockParam);

  var rampSampler = pack.createObject('Sampler');
  rampSampler.texture = particleSystem.defaultRampTexture;
  rampSampler.addressModeU = o3d.Sampler.CLAMP;

  var colorSampler = pack.createObject('Sampler');
  colorSampler.texture = opt_texture || particleSystem.defaultColorTexture;
  colorSampler.addressModeU = o3d.Sampler.CLAMP;
  colorSampler.addressModeV = o3d.Sampler.CLAMP;

  material.getParam('rampSampler').value = rampSampler;
  material.getParam('colorSampler').value = colorSampler;

  var vertexBuffer = pack.createObject('VertexBuffer');
  var uvLifeTimeFrameStartField = vertexBuffer.createField('FloatField', 4);
  var positionStartTimeField = vertexBuffer.createField('FloatField', 4);
  var velocityStartSizeField = vertexBuffer.createField('FloatField', 4);
  var accelerationEndSizeField = vertexBuffer.createField('FloatField', 4);
  var spinStartSpinSpeedField = vertexBuffer.createField('FloatField', 4);
  var orientationField = vertexBuffer.createField('FloatField', 4);
  var colorMultField = vertexBuffer.createField('FloatField', 4);

  var indexBuffer = pack.createObject('IndexBuffer');

  var streamBank = pack.createObject('StreamBank');
  streamBank.setVertexStream(o3d.Stream.POSITION, 0,
                             uvLifeTimeFrameStartField, 0);
  streamBank.setVertexStream(o3d.Stream.TEXCOORD, 0,
                             positionStartTimeField, 0);
  streamBank.setVertexStream(o3d.Stream.TEXCOORD, 1,
                             velocityStartSizeField, 0);
  streamBank.setVertexStream(o3d.Stream.TEXCOORD, 2,
                             accelerationEndSizeField, 0);
  streamBank.setVertexStream(o3d.Stream.TEXCOORD, 3,
                             spinStartSpinSpeedField, 0);
  streamBank.setVertexStream(o3d.Stream.TEXCOORD, 4,
                             orientationField, 0);
  streamBank.setVertexStream(o3d.Stream.COLOR, 0,
                             colorMultField, 0);

  var shape = pack.createObject('Shape');
  var primitive = pack.createObject('Primitive');
  primitive.material = material;
  primitive.owner = shape;
  primitive.streamBank = streamBank;
  primitive.indexBuffer = indexBuffer;
  primitive.primitiveType = o3d.Primitive.TRIANGLELIST;
  primitive.createDrawElement(pack, null);

  this.vertexBuffer_ = vertexBuffer;
  this.uvLifeTimeFrameStartField_ = uvLifeTimeFrameStartField;
  this.positionStartTimeField_ = positionStartTimeField;
  this.velocityStartSizeField_ = velocityStartSizeField;
  this.accelerationEndSizeField_ = accelerationEndSizeField;
  this.spinStartSpinSpeedField_ = spinStartSpinSpeedField;
  this.orientationField_ = orientationField;
  this.colorMultField_ = colorMultField;
  this.indexBuffer_ = indexBuffer;
  this.streamBank_ = streamBank;
  this.primitive_ = primitive;
  this.rampSampler_ = rampSampler;
  this.rampTexture_ = particleSystem.defaultRampTexture;
  this.colorSampler_ = colorSampler;

  /**
   * The particle system managing this emitter.
   * @type {!o3djs.particles.ParticleSystem}
   */
  this.particleSystem = particleSystem;

  /**
   * The Shape used to render these particles.
   * @type {!o3d.Shape}
   */
  this.shape = shape;

  /**
   * The material used by this emitter.
   * @type {!o3d.Matrerial}
   */
  this.material = material;

  /**
   * The param that is the source for the time for this emitter.
   * @type {!o3d.ParamFloat}
   */
  this.clockParam = opt_clockParam;
};

/**
 * Sets the blend state for the particles.
 * You can use this to set the emitter to draw with BLEND, ADD, SUBTRACT, etc.
 * @param {o3djs.particles.ParticleStateIds} stateId The state you want.
 */
o3djs.particles.ParticleEmitter.prototype.setState = function(stateId) {
  this.material.state = this.particleSystem.particleStates[stateId];
};

/**
 * Sets the colorRamp for the particles.
 * The colorRamp is used as a multiplier for the texture. When a particle
 * starts it is multiplied by the first color, as it ages to progressed
 * through the colors in the ramp.
 *
 * <pre>
 * particleEmitter.setColorRamp([
 *   1, 0, 0, 1,    // red
 *   0, 1, 0, 1,    // green
 *   1, 0, 1, 0]);  // purple but with zero alpha
 * </pre>
 *
 * The code above sets the particle to start red, change to green then
 * fade out while changing to purple.
 *
 * @param {!Array.<number>} colorRamp An array of color values in
 *     the form RGBA.
 */
o3djs.particles.ParticleEmitter.prototype.setColorRamp = function(colorRamp) {
  var width = colorRamp.length / 4;
  if (width % 1 != 0) {
    throw 'colorRamp must have multiple of 4 entries';
  }

  if (this.rampTexture_ == this.particleSystem.defaultRampTexture) {
    this.rampTexture_ = null;
  }

  if (this.rampTexture_ && this.rampTexture_.width != width) {
    this.particleSystem.pack.removeObject(this.rampTexture_);
    this.rampTexture_ = null;
  }

  if (!this.rampTexture_) {
    this.rampTexture_ = this.particleSystem.pack.createTexture2D(
        width, 1, o3djs.base.o3d.Texture.ARGB8, 1, false);
  }

  this.rampTexture_.set(0, colorRamp);
  this.rampSampler_.texture = this.rampTexture_;
};

/**
 * Validates and adds missing particle parameters.
 * @param {!o3djs.particles.ParticleSpec} parameters The parameters to validate.
 */
o3djs.particles.ParticleEmitter.prototype.validateParameters = function(
    parameters) {
  var defaults = new o3djs.particles.ParticleSpec();
  for (var key in parameters) {
    if (typeof defaults[key] === 'undefined') {
      throw 'unknown particle parameter "' + key + '"';
    }
  }
  for (var key in defaults) {
    if (typeof parameters[key] === 'undefined') {
      parameters[key] = defaults[key];
    }
  }
};

/**
 * Creates particles.
 * @private
 * @param {number} firstParticleIndex Index of first particle to create.
 * @param {number} numParticles The number of particles to create.
 * @param {!o3djs.particles.ParticleSpec} parameters The parameters for the
 *     emitters.
 * @param {!function(number, !o3djs.particles.ParticleSpec): void}
 *     opt_perParticleParamSetter A function that is called for each particle to
 *     allow it's parameters to be adjusted per particle. The number is the
 *     index of the particle being created, in other words, if numParticles is
 *     20 this value will be 0 to 19. The ParticleSpec is a spec for this
 *     particular particle. You can set any per particle value before returning.
 */
o3djs.particles.ParticleEmitter.prototype.createParticles_ = function(
    firstParticleIndex,
    numParticles,
    parameters,
    opt_perParticleParamSetter) {
  var uvLifeTimeFrameStart = this.uvLifeTimeFrameStart_;
  var positionStartTime = this.positionStartTime_;
  var velocityStartSize = this.velocityStartSize_;
  var accelerationEndSize = this.accelerationEndSize_;
  var spinStartSpinSpeed = this.spinStartSpinSpeed_;
  var orientation = this.orientation_;
  var colorMults = this.colorMults_;

  // Set the globals.
  this.material.effect =
      this.particleSystem.effects[parameters.billboard ? 1 : 0];
  this.material.getParam('timeRange').value = parameters.timeRange;
  this.material.getParam('numFrames').value = parameters.numFrames;
  this.material.getParam('frameDuration').value = parameters.frameDuration;
  this.material.getParam('worldVelocity').value = parameters.worldVelocity;
  this.material.getParam('worldAcceleration').value =
      parameters.worldAcceleration;

  var random = this.particleSystem.randomFunction_;

  var plusMinus = function(range) {
    return (random() - 0.5) * range * 2;
  };

  // TODO: change to not allocate.
  var plusMinusVector = function(range) {
    var v = [];
    for (var ii = 0; ii < range.length; ++ii) {
      v.push(plusMinus(range[ii]));
    }
    return v;
  };

  for (var ii = 0; ii < numParticles; ++ii) {
    if (opt_perParticleParamSetter) {
      opt_perParticleParamSetter(ii, parameters);
    }
    var pLifeTime = parameters.lifeTime;
    var pStartTime = (parameters.startTime === null) ?
        (ii * parameters.lifeTime / numParticles) : parameters.startTime;
    var pFrameStart =
        parameters.frameStart + plusMinus(parameters.frameStartRange);
    var pPosition = o3djs.math.addVector(
        parameters.position, plusMinusVector(parameters.positionRange));
    var pVelocity = o3djs.math.addVector(
        parameters.velocity, plusMinusVector(parameters.velocityRange));
    var pAcceleration = o3djs.math.addVector(
        parameters.acceleration,
        plusMinusVector(parameters.accelerationRange));
    var pColorMult = o3djs.math.addVector(
        parameters.colorMult, plusMinusVector(parameters.colorMultRange));
    var pSpinStart =
        parameters.spinStart + plusMinus(parameters.spinStartRange);
    var pSpinSpeed =
        parameters.spinSpeed + plusMinus(parameters.spinSpeedRange);
    var pStartSize =
        parameters.startSize + plusMinus(parameters.startSizeRange);
    var pEndSize = parameters.endSize + plusMinus(parameters.endSizeRange);
    var pOrientation = parameters.orientation;

    // make each corner of the particle.
    for (var jj = 0; jj < 4; ++jj) {
      var offset0 = (ii * 4 + jj) * 4;
      var offset1 = offset0 + 1;
      var offset2 = offset0 + 2;
      var offset3 = offset0 + 3;

      uvLifeTimeFrameStart[offset0] = o3djs.particles.CORNERS_[jj][0];
      uvLifeTimeFrameStart[offset1] = o3djs.particles.CORNERS_[jj][1];
      uvLifeTimeFrameStart[offset2] = pLifeTime;
      uvLifeTimeFrameStart[offset3] = pFrameStart;

      positionStartTime[offset0] = pPosition[0];
      positionStartTime[offset1] = pPosition[1];
      positionStartTime[offset2] = pPosition[2];
      positionStartTime[offset3] = pStartTime;

      velocityStartSize[offset0] = pVelocity[0];
      velocityStartSize[offset1] = pVelocity[1];
      velocityStartSize[offset2] = pVelocity[2];
      velocityStartSize[offset3] = pStartSize;

      accelerationEndSize[offset0] = pAcceleration[0];
      accelerationEndSize[offset1] = pAcceleration[1];
      accelerationEndSize[offset2] = pAcceleration[2];
      accelerationEndSize[offset3] = pEndSize;

      spinStartSpinSpeed[offset0] = pSpinStart;
      spinStartSpinSpeed[offset1] = pSpinSpeed;
      spinStartSpinSpeed[offset2] = 0;
      spinStartSpinSpeed[offset3] = 0;

      orientation[offset0] = pOrientation[0];
      orientation[offset1] = pOrientation[1];
      orientation[offset2] = pOrientation[2];
      orientation[offset3] = pOrientation[3];

      colorMults[offset0] = pColorMult[0];
      colorMults[offset1] = pColorMult[1];
      colorMults[offset2] = pColorMult[2];
      colorMults[offset3] = pColorMult[3];
    }
  }

  firstParticleIndex *= 4;
  this.uvLifeTimeFrameStartField_.setAt(
      firstParticleIndex,
      uvLifeTimeFrameStart);
  this.positionStartTimeField_.setAt(
      firstParticleIndex,
      positionStartTime);
  this.velocityStartSizeField_.setAt(
      firstParticleIndex,
      velocityStartSize);
  this.accelerationEndSizeField_.setAt(
      firstParticleIndex,
      accelerationEndSize);
  this.spinStartSpinSpeedField_.setAt(
      firstParticleIndex,
      spinStartSpinSpeed);
  this.orientationField_.setAt(
      firstParticleIndex,
      orientation);
  this.colorMultField_.setAt(
      firstParticleIndex,
      colorMults);
};

/**
 * Allocates particles.
 * @private
 * @param {number} numParticles Number of particles to allocate.
 */
o3djs.particles.ParticleEmitter.prototype.allocateParticles_ = function(
    numParticles) {
  if (this.vertexBuffer_.numElements != numParticles * 4) {
    this.vertexBuffer_.allocateElements(numParticles * 4);

    var indices = [];
    for (var ii = 0; ii < numParticles; ++ii) {
      // Make 2 triangles for the quad.
      var startIndex = ii * 4
      indices.push(startIndex + 0, startIndex + 1, startIndex + 2);
      indices.push(startIndex + 0, startIndex + 2, startIndex + 3);
    }
    this.indexBuffer_.set(indices);

    // We keep these around to avoid memory allocations for trails.
    this.uvLifeTimeFrameStart_ = [];
    this.positionStartTime_ = [];
    this.velocityStartSize_ = [];
    this.accelerationEndSize_ = [];
    this.spinStartSpinSpeed_ = [];
    this.orientation_ = [];
    this.colorMults_ = [];
  }

  this.primitive_.numberPrimitives = numParticles * 2;
  this.primitive_.numberVertices = numParticles * 4;
};

/**
 * Sets the parameters of the particle emitter.
 *
 * Each of these parameters are in pairs. The used to create a table
 * of particle parameters. For each particle a specfic value is
 * set like this
 *
 * particle.field = value + Math.random() - 0.5 * valueRange * 2;
 *
 * or in English
 *
 * particle.field = value plus or minus valueRange.
 *
 * So for example, if you wanted a value from 10 to 20 you'd pass 15 for value
 * and 5 for valueRange because
 *
 * 15 + or - 5  = (10 to 20)
 *
 * @param {!o3djs.particles.ParticleSpec} parameters The parameters for the
 *     emitters.
 * @param {!function(number, !o3djs.particles.ParticleSpec): void}
 *     opt_perParticleParamSetter A function that is called for each particle to
 *     allow it's parameters to be adjusted per particle. The number is the
 *     index of the particle being created, in other words, if numParticles is
 *     20 this value will be 0 to 19. The ParticleSpec is a spec for this
 *     particular particle. You can set any per particle value before returning.
 */
o3djs.particles.ParticleEmitter.prototype.setParameters = function(
    parameters,
    opt_perParticleParamSetter) {
  this.validateParameters(parameters);

  var numParticles = parameters.numParticles;

  this.allocateParticles_(numParticles);

  this.createParticles_(
      0,
      numParticles,
      parameters,
      opt_perParticleParamSetter);
};

/**
 * Creates a OneShot particle emitter instance.
 * You can use this for dust puffs, explosions, fireworks, etc...
 * @param {!o3d.Transform} opt_parent The parent for the oneshot.
 * @return {!o3djs.particles.OneShot} A OneShot object.
 */
o3djs.particles.ParticleEmitter.prototype.createOneShot = function(opt_parent) {
  return new o3djs.particles.OneShot(this, opt_parent);
};

/**
 * An object to manage a particle emitter instance as a one shot. Examples of
 * one shot effects are things like an explosion, some fireworks.
 * @constructor
 * @param {!o3djs.particles.ParticleEmitter} emitter The emitter to use for the
 *     one shot.
 * @param {!o3d.Transform} opt_parent The parent for this one shot.
 */
o3djs.particles.OneShot = function(emitter, opt_parent) {
  var pack = emitter.particleSystem.pack;
  this.emitter_ = emitter;

  /**
   * Transform for OneShot.
   * @type {!o3d.Transform}
   */
  this.transform = pack.createObject('Transform');
  this.transform.visible = false;
  this.transform.addShape(emitter.shape);
  this.timeOffsetParam_ =
      this.transform.createParam('timeOffset', 'ParamFloat');
  if (opt_parent) {
    this.setParent(opt_parent);
  }
};

/**
 * Sets the parent transform for this OneShot.
 * @param {!o3d.Transform} parent The parent for this one shot.
 */
o3djs.particles.OneShot.prototype.setParent = function(parent) {
  this.transform.parent = parent;
};

/**
 * Triggers the oneshot.
 *
 * Note: You must have set the parent either at creation, with setParent, or by
 * passing in a parent here.
 *
 * @param {!o3djs.math.Vector3} opt_position The position of the one shot
 *     relative to its parent.
 * @param {!o3d.Transform} opt_parent The parent for this one shot.
 */
o3djs.particles.OneShot.prototype.trigger = function(opt_position, opt_parent) {
  if (opt_parent) {
    this.setParent(opt_parent);
  }
  if (opt_position) {
    this.transform.identity();
    this.transform.translate(opt_position);
  }
  this.transform.visible = true;
  this.timeOffsetParam_.value = this.emitter_.clockParam.value;
};

/**
 * A type of emitter to use for particle effects that leave trails like exhaust.
 * @constructor
 * @extends {o3djs.particles.ParticleEmitter}
 * @param {!o3djs.particles.ParticleSystem} particleSystem The particle system
 *     to manage this emitter.
 * @param {!o3d.Transform} parent Transform to put emitter on.
 * @param {number} maxParticles Maximum number of particles to appear at once.
 * @param {!o3djs.particles.ParticleSpec} parameters The parameters used to
 *     generate particles.
 * @param {!o3d.Texture} opt_texture The texture to use for the particles.
 *     If you don't supply a texture a default is provided.
 * @param {!function(number, !o3djs.particles.ParticleSpec): void}
 *     opt_perParticleParamSetter A function that is called for each particle to
 *     allow it's parameters to be adjusted per particle. The number is the
 *     index of the particle being created, in other words, if numParticles is
 *     20 this value will be 0 to 19. The ParticleSpec is a spec for this
 *     particular particle. You can set any per particle value before returning.
 * @param {!o3d.ParamFloat} opt_clockParam A ParamFloat to be the clock for
 *     the emitter.
 */
o3djs.particles.Trail = function(
    particleSystem,
    parent,
    maxParticles,
    parameters,
    opt_texture,
    opt_perParticleParamSetter,
    opt_clockParam) {
  o3djs.particles.ParticleEmitter.call(
      this, particleSystem, opt_texture, opt_clockParam);

  var pack = particleSystem.pack;

  this.allocateParticles_(maxParticles);
  this.validateParameters(parameters);

  this.parameters = parameters;
  this.perParticleParamSetter = opt_perParticleParamSetter;
  this.birthIndex_ = 0;
  this.maxParticles_ = maxParticles;

  /**
   * Transform for OneShot.
   * @type {!o3d.Transform}
   */
  this.transform = pack.createObject('Transform');
  this.transform.addShape(this.shape);

  this.transform.parent = parent;
};

o3djs.base.inherit(o3djs.particles.Trail, o3djs.particles.ParticleEmitter);

/**
 * Births particles from this Trail.
 * @param {!o3djs.math.Vector3} position Position to birth particles at.
 */
o3djs.particles.Trail.prototype.birthParticles = function(position) {
  var numParticles = this.parameters.numParticles;
  this.parameters.startTime = this.clockParam.value;
  this.parameters.position = position;
  while (this.birthIndex_ + numParticles >= this.maxParticles_) {
    var numParticlesToEnd = this.maxParticles_ - this.birthIndex_;
    this.createParticles_(this.birthIndex_,
                          numParticlesToEnd,
                          this.parameters,
                          this.perParticleParamSetter);
    numParticles -= numParticlesToEnd;
    this.birthIndex_ = 0;
  }
  this.createParticles_(this.birthIndex_,
                        numParticles,
                        this.parameters,
                        this.perParticleParamSetter);
  this.birthIndex_ += numParticles;
};

redpajama_set_name: RedPajamaGithub


[color:1b92=blue][b]What I Raffle:cybearguide ski cap, Washington Wig, an aviator hat, frog tee, Pawsome Party Tee, friendship tee, Fabulouse tee, and blue mittens.
The way you said it. I quote,"No I have them all." The tone of voice.
I used a tone of voice like happy voice you don't know what tone of voice I used anyway I was gonna ask could I help?
[quote:fbab="CarolinaApril53"]We are pretty sure you used a snotty voice, but anyways... [/quote]If I used a snotty voice wouldn't I sneezed and it would go all over your computer/laptop?!?! LOL!
Look the word snotty up. NOTHING like you just have said. That just insulted me.
Don't you people have common sense??? It was a joke!
I was gonna say "Can I help?"
OK I'm sorry Claudie Carolina and everyone I acted stupid to!
If you don't have anything nice to say, don't say anything at all. Thank You.
Yeah, and apologizing now is not going to do ANYTHING. If your want to say something mean then please leave. We do not want negative people on this forum.