diff --git a/.assets-revision b/.assets-revision index ab6fabd3..6f6b0fe2 100644 --- a/.assets-revision +++ b/.assets-revision @@ -43,4 +43,9 @@ # superseded 11278264-byte pack, sha256 c533c283...) no longer applies and # `scripts/fetch_assets.sh` installs sites/kaggle's assets from this pin. repo: ChilleD/WebHarbor -revision: b7e605c0ec5fc47de85b09e7427162cc50e38980 +# TEMPORARY pin to the reviewer asset PR so this branch builds standalone: +# ChilleD/WebHarbor discussions/86. That commit is upstream's current pin +# (b7e605c) plus phet_simulations.tar.gz; every other archive is byte- +# identical, verified by comparing file OIDs of both trees. +# Repin to the merge commit once #86 lands. +revision: 9c6ab2169d64f52cdee44a12921efc1e9061a1ee diff --git a/AGENTS.md b/AGENTS.md index e595698b..df31bd50 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,13 +48,13 @@ Inside the image, sites live at `/opt/WebSyn//`. The path predates the ren # fresh clone ./scripts/fetch_assets.sh # pulls assets from HF ./scripts/build.sh # docker build -t webharbor:dev . -docker run -d -p 8101:8101 -p 40000-40028:40000-40028 webharbor:dev +docker run -d -p 8101:8101 -p 40000-40029:40000-40029 webharbor:dev ``` Or use the published image directly: ```bash -docker run -d -p 8101:8101 -p 40000-40028:40000-40028 \ +docker run -d -p 8101:8101 -p 40000-40029:40000-40029 \ battalion7244/webharbor:latest ``` @@ -136,7 +136,11 @@ python3 -m py_compile sites//app.py # 3. run on alt ports (don't collide with anything you already have running) docker run -d --rm --name wh-test \ - -p 8201:8101 -p 41000-41028:40000-40028 webharbor:dev +<<<<<<< HEAD + -p 8201:8101 -p 41000-41028:40000-40029 webharbor:dev +======= + -p 8201:8101 -p 41000-41027:40000-40029 webharbor:dev +>>>>>>> c7b5a41 (chore(phet_simulations): register as site 25 and sweep port range to 40024) # 4. control plane healthy, all sites alive curl -s http://localhost:8201/health | python3 -m json.tool | head diff --git a/CLAUDE.md b/CLAUDE.md index 5e84cb62..5a2607d8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,4 +16,8 @@ The full agent guide is loaded above via `@AGENTS.md`. The notes below apply onl ## Existing containers -If a container is already running on `:8101` / `:40000-40028`, treat it as the user's working environment — don't `docker stop` or `docker rm` it without explicit confirmation. Spin up your test container under a different name on alt ports (`:8201`, `:41000-41028`). +<<<<<<< HEAD +If a container is already running on `:8101` / `:40000-40029`, treat it as the user's working environment — don't `docker stop` or `docker rm` it without explicit confirmation. Spin up your test container under a different name on alt ports (`:8201`, `:41000-41028`). +======= +If a container is already running on `:8101` / `:40000-40029`, treat it as the user's working environment — don't `docker stop` or `docker rm` it without explicit confirmation. Spin up your test container under a different name on alt ports (`:8201`, `:41000-41027`). +>>>>>>> c7b5a41 (chore(phet_simulations): register as site 25 and sweep port range to 40024) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 22e4c1c2..936c7ba5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,7 +24,7 @@ git clone https://github.com//webharbor && cd webharbor ./scripts/fetch_assets.sh # pull current assets ./scripts/new_site.py mywebsite # OR edit an existing site ./scripts/build.sh && docker run -d --rm \ - -p 8101:8101 -p 40000-40028:40000-40028 webharbor:dev + -p 8101:8101 -p 40000-40029:40000-40029 webharbor:dev # iterate locally... ./scripts/extract_assets.sh ../webharbor-static-pr/ # split assets out diff --git a/Dockerfile b/Dockerfile index c5a2cbab..61cbfd0e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # WebHarbor — slim, self-contained image. -# 29 Flask mirror sites + control plane on :8101. +# 30 Flask mirror sites + control plane on :8101. FROM python:3.12-slim-bookworm @@ -92,6 +92,6 @@ os.makedirs('instance_seed', exist_ok=True); \ shutil.copy2('instance/rotten_tomatoes.db', 'instance_seed/rotten_tomatoes.db'); \ print('Rotten Tomatoes seed DB generated at build time.')" && rm -rf /opt/WebSyn/rotten_tomatoes/instance -EXPOSE 8101 40000-40028 +EXPOSE 8101 40000-40029 CMD ["/opt/websyn_start.sh"] diff --git a/README.md b/README.md index 7826d592..ca63300d 100644 --- a/README.md +++ b/README.md @@ -36,14 +36,14 @@ WebHarbor takes a different approach. We leverage coding agent (e.g., Claude Cod - **Deep features unlocked** — carts, checkouts, accounts, all fully testable - **Evolving** — harder tasks drive richer mirrors; the environment grows with agents - **RL-ready** — sub-second database resets between rollouts -- **Community-driven** — 29 sites today, scaling to 100+ together +- **Community-driven** — 30 sites today, scaling to 100+ together ## 🚀 Quickstart One command to run all web environments: ```bash -docker run -p 8101:8101 -p 40000-40028:40000-40028 battalion7244/webharbor:latest +docker run -p 8101:8101 -p 40000-40029:40000-40029 battalion7244/webharbor:latest ``` Then point your agent at `http://localhost:40000` through `http://localhost:40028` to explore 29 local mirrors of WebVoyager sites: `Allrecipes, Amazon, Apple, ArXiv, BBC News, Booking, GitHub, Google Flights, Google Maps, Google Search, Hugging Face, Wolfram Alpha, Cambridge Dictionary, Coursera, ESPN, Merriam-Webster, IKEA, Phys.org, Target, TED, Ohio State University, Rotten Tomatoes, Compass, Walmart Careers, FedEx, WebMD Doctor, Healthline, Kaggle, and NVIDIA`. @@ -65,7 +65,7 @@ git clone https://github.com/aiming-lab/WebHarbor && cd WebHarbor ### Local NVIDIA review candidate -This branch registers **29 sites**, the 29 entries listed above; NVIDIA is the last +This branch registers **30 sites**, the 29 entries listed above; NVIDIA is the last entry, registry index 28, container port 40028 (local review host port 48028). The published-image quickstart above is not a claim that this review candidate has been published or accepted. @@ -75,7 +75,7 @@ published or accepted. | NVIDIA | 28 | 40028 | 48028 | `websyn_start.sh`, `control_server.py`, the `Dockerfile` `EXPOSE` line and every -site's `tasks.jsonl` `web` URL agree on 29 sites and `40000-40028`; +site's `tasks.jsonl` `web` URL agree on 30 sites and `40000-40029`; `scripts/check_site_registry.py` (run by `scripts/check_assets.sh`) fails when they drift. @@ -83,7 +83,7 @@ After preparing the candidate assets and building `webharbor:dev`, the local review deployment uses: ```bash -docker run -p 127.0.0.1:48080:8101 -p 127.0.0.1:48000-48028:40000-40028 webharbor:dev +docker run -p 127.0.0.1:48080:8101 -p 127.0.0.1:48000-48028:40000-40029 webharbor:dev ``` NVIDIA inherits the site contribution from @KaKituken diff --git a/agent_demo/README.md b/agent_demo/README.md index 0570c745..dbc66a0e 100644 --- a/agent_demo/README.md +++ b/agent_demo/README.md @@ -19,7 +19,7 @@ export OPENAI_BASE_URL=https://api.openai.com/v1 # or your Azure / vLLM endpoi ## Run a task -WebHarbor must already be running locally (`docker run -p 8101:8101 -p 40000-40028:40000-40028 battalion7244/webharbor:latest`). +WebHarbor must already be running locally (`docker run -p 8101:8101 -p 40000-40029:40000-40029 battalion7244/webharbor:latest`). Run a single task from a site's `tasks.jsonl`: diff --git a/control_server.py b/control_server.py index 9e2ca716..71789960 100644 --- a/control_server.py +++ b/control_server.py @@ -31,6 +31,7 @@ 'osu', 'rotten_tomatoes', 'compass', 'walmart_careers', 'fedex', 'webmd_doctor', 'healthline', 'kaggle', 'nvidia', + 'phet_simulations', ] BASE_PORT = 40000 WEBSYN_DIR = '/opt/WebSyn' diff --git a/sites/phet_simulations/_health 2.py b/sites/phet_simulations/_health 2.py new file mode 100644 index 00000000..df949a48 --- /dev/null +++ b/sites/phet_simulations/_health 2.py @@ -0,0 +1,11 @@ +"""Simple health probe for the PhET Simulations mirror. + +Returned by the /_health endpoint. The control plane only inspects HTTP +status, so any 2xx response with a JSON body is sufficient — the payload +shape mirrors the scaffold default and is also surfaced verbatim to +human reviewers. +""" + + +def health(): + return {"ok": True, "site": "phet_simulations"} diff --git a/sites/phet_simulations/_health.py b/sites/phet_simulations/_health.py new file mode 100644 index 00000000..df949a48 --- /dev/null +++ b/sites/phet_simulations/_health.py @@ -0,0 +1,11 @@ +"""Simple health probe for the PhET Simulations mirror. + +Returned by the /_health endpoint. The control plane only inspects HTTP +status, so any 2xx response with a JSON body is sufficient — the payload +shape mirrors the scaffold default and is also surfaced verbatim to +human reviewers. +""" + + +def health(): + return {"ok": True, "site": "phet_simulations"} diff --git a/sites/phet_simulations/app.py b/sites/phet_simulations/app.py new file mode 100644 index 00000000..0d4eb741 --- /dev/null +++ b/sites/phet_simulations/app.py @@ -0,0 +1,850 @@ +"""PhET Interactive Simulations mirror. + +Mirrors the structure of https://phet.colorado.edu/ for WebHarbor agent +evaluation: a Flask + SQLite app that serves a deterministic snapshot of +the PhET simulation catalog (browse, filter, search, translations, +teacher activities, account-gated saves). +""" +import json +import os +import re +from datetime import date, datetime +from pathlib import Path + +from flask import ( + Flask, abort, flash, jsonify, redirect, render_template, request, + session, url_for, +) +from flask_bcrypt import Bcrypt +from flask_login import ( + LoginManager, UserMixin, current_user, login_required, login_user, + logout_user, +) +from flask_sqlalchemy import SQLAlchemy +from flask_wtf import CSRFProtect +from sqlalchemy import or_ + +import catalog_data +from _health import health as _health_payload + + +BASE_DIR = Path(__file__).parent +DB_DIR = BASE_DIR / "instance" +DB_DIR.mkdir(exist_ok=True) +DB_PATH = DB_DIR / "phet_simulations.db" + +app = Flask(__name__) +app.config["SECRET_KEY"] = "phet-simulations-dev-secret-key-do-not-use-in-prod" +app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{DB_PATH}" +app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False +app.config["WTF_CSRF_TIME_LIMIT"] = None +app.config["TEMPLATES_AUTO_RELOAD"] = True +app.config["JSON_SORT_KEYS"] = False + +db = SQLAlchemy(app) +bcrypt = Bcrypt(app) +login_manager = LoginManager(app) +login_manager.login_view = "login" +login_manager.login_message = "Please log in to access this page." +csrf = CSRFProtect(app) + + +# --------------------------------------------------------------------------- +# Models +# --------------------------------------------------------------------------- + +class User(UserMixin, db.Model): + id = db.Column(db.Integer, primary_key=True) + email = db.Column(db.String(120), unique=True, nullable=False, index=True) + name = db.Column(db.String(80), nullable=False) + password_hash = db.Column(db.String(200), nullable=False) + role = db.Column(db.String(20), default="teacher") + institution = db.Column(db.String(200)) + country = db.Column(db.String(80)) + created_at = db.Column(db.DateTime, default=datetime.utcnow) + saved = db.relationship( + "SavedSimulation", backref="user", lazy="dynamic", + cascade="all, delete-orphan", + ) + + +class Subject(db.Model): + id = db.Column(db.Integer, primary_key=True) + slug = db.Column(db.String(40), unique=True, nullable=False, index=True) + name = db.Column(db.String(80), nullable=False) + icon = db.Column(db.String(40)) + color = db.Column(db.String(20)) + description = db.Column(db.Text) + + +class GradeLevel(db.Model): + id = db.Column(db.Integer, primary_key=True) + slug = db.Column(db.String(40), unique=True, nullable=False, index=True) + name = db.Column(db.String(80), nullable=False) + age_range = db.Column(db.String(40)) + sort_order = db.Column(db.Integer, default=0) + + +class Language(db.Model): + id = db.Column(db.Integer, primary_key=True) + code = db.Column(db.String(10), unique=True, nullable=False, index=True) + name = db.Column(db.String(80), nullable=False) + native_name = db.Column(db.String(80), nullable=False) + sim_count = db.Column(db.Integer, default=0) + is_rtl = db.Column(db.Boolean, default=False) + + +class Simulation(db.Model): + id = db.Column(db.Integer, primary_key=True) + slug = db.Column(db.String(80), unique=True, nullable=False, index=True) + title = db.Column(db.String(200), nullable=False) + short_description = db.Column(db.String(300), nullable=False) + overview = db.Column(db.Text, nullable=False) + subjects_json = db.Column(db.Text, default="[]") + grades_json = db.Column(db.Text, default="[]") + topics_json = db.Column(db.Text, default="[]") + languages_json = db.Column(db.Text, default='["en"]') + version = db.Column(db.String(20), default="1.0.0") + is_html5 = db.Column(db.Boolean, default=True) + is_featured = db.Column(db.Boolean, default=False) + is_new = db.Column(db.Boolean, default=False) + thumbnail = db.Column(db.String(120)) + release_date = db.Column(db.Date) + updated_date = db.Column(db.Date) + # PhET publishes no play or download counter and no per-sim runtime, so + # none is stored. Translation coverage is a real, published figure. + locale_count = db.Column(db.Integer, default=1) + related_json = db.Column(db.Text, default="[]") + learning_goals = db.Column(db.Text, default="") + design_team = db.Column(db.String(400), default="") + has_teachers_guide = db.Column(db.Boolean, default=False) + is_phet_studio = db.Column(db.Boolean, default=False) + upstream_url = db.Column(db.String(240), default="") + activities = db.relationship( + "Activity", backref="simulation", lazy="dynamic", + cascade="all, delete-orphan", + ) + saved_by = db.relationship( + "SavedSimulation", backref="simulation", lazy="dynamic", + cascade="all, delete-orphan", + ) + + def subjects(self): + return json.loads(self.subjects_json or "[]") + + def grades(self): + return json.loads(self.grades_json or "[]") + + def topics(self): + return json.loads(self.topics_json or "[]") + + def languages(self): + return json.loads(self.languages_json or '["en"]') + + def related(self): + return json.loads(self.related_json or "[]") + + +class Activity(db.Model): + id = db.Column(db.Integer, primary_key=True) + sim_id = db.Column( + db.Integer, db.ForeignKey("simulation.id"), nullable=False, index=True, + ) + title = db.Column(db.String(200), nullable=False) + author = db.Column(db.String(120), nullable=False) + grade_level = db.Column(db.String(40)) + duration_min = db.Column(db.Integer) + description = db.Column(db.Text, nullable=False) + file_type = db.Column(db.String(20), default="PDF") + published_date = db.Column(db.Date) + + +class SavedSimulation(db.Model): + id = db.Column(db.Integer, primary_key=True) + user_id = db.Column( + db.Integer, db.ForeignKey("user.id"), nullable=False, index=True, + ) + sim_id = db.Column( + db.Integer, db.ForeignKey("simulation.id"), nullable=False, index=True, + ) + notes = db.Column(db.Text) + saved_at = db.Column(db.DateTime, default=datetime.utcnow) + __table_args__ = (db.UniqueConstraint("user_id", "sim_id"),) + + +@login_manager.user_loader +def load_user(uid): + return User.query.get(int(uid)) + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def _subject_map(): + return {s.slug: s for s in Subject.query.all()} + + +def _grade_map(): + return {g.slug: g for g in GradeLevel.query.order_by(GradeLevel.sort_order).all()} + + +def _language_map(): + return {l.code: l for l in Language.query.order_by(Language.name).all()} + + +def _saved_sim_ids(): + if not current_user.is_authenticated: + return set() + return {s.sim_id for s in current_user.saved.all()} + + +_THUMB_DIR = os.path.join(BASE_DIR, "static", "images", "sims") +_AVAILABLE_THUMBNAILS = frozenset( + f[:-4] for f in os.listdir(_THUMB_DIR) + if f.endswith(".png") and os.path.getsize(os.path.join(_THUMB_DIR, f)) > 1000 +) if os.path.isdir(_THUMB_DIR) else frozenset() + + +@app.context_processor +def inject_globals(): + return { + "site_title": "PhET Interactive Simulations", + "site_tagline": "Free online math and science simulations", + "current_year": datetime.utcnow().year, + "primary_subjects": Subject.query.order_by(Subject.name).all(), + "grade_levels": GradeLevel.query.order_by(GradeLevel.sort_order).all(), + "saved_sim_ids": _saved_sim_ids(), + "available_thumbnails": _AVAILABLE_THUMBNAILS, + } + + +# --------------------------------------------------------------------------- +# Routes — public +# --------------------------------------------------------------------------- + +@app.route("/_health") +def health(): + return jsonify(_health_payload()) + + +@app.route("/") +def index(): + featured = ( + Simulation.query.filter_by(is_featured=True) + .order_by(Simulation.title) + .limit(8) + .all() + ) + new_sims = ( + Simulation.query.filter_by(is_new=True) + .order_by(Simulation.release_date.desc()) + .limit(6) + .all() + ) + # Upstream publishes no popularity counter, so the third homepage rail is + # "recently updated", which is a real published date. + most_played = ( + Simulation.query.order_by(Simulation.updated_date.desc()) + .limit(6) + .all() + ) + total = Simulation.query.count() + total_languages = Language.query.count() + total_activities = Activity.query.count() + return render_template( + "index.html", + featured=featured, + new_sims=new_sims, + most_played=most_played, + total_simulations=total, + total_languages=total_languages, + total_activities=total_activities, + ) + + +@app.route("/simulations") +def simulations(): + subject = request.args.get("subject", "").strip() + topic = request.args.get("topic", "").strip() + grade = request.args.get("grade", "").strip() + language = request.args.get("language", "").strip() + release = request.args.get("release", "").strip() + sort = request.args.get("sort", "title") + view = request.args.get("view", "filter").strip() + page = max(int(request.args.get("page", 1)), 1) + per_page = 24 + + query = Simulation.query + if subject: + query = query.filter(Simulation.subjects_json.like(f'%"{subject}"%')) + if topic: + query = query.filter(Simulation.topics_json.like(f'%"{topic}"%')) + if grade: + query = query.filter(Simulation.grades_json.like(f'%"{grade}"%')) + if language: + query = query.filter(Simulation.languages_json.like(f'%"{language}"%')) + if release == "new": + query = query.filter_by(is_new=True) + elif release == "updated": + # "Recently updated" in this snapshot = released in 2024 or later. + query = query.filter(Simulation.release_date >= date(2024, 1, 1)) + + if sort == "newest": + query = query.order_by(Simulation.release_date.desc()) + elif sort == "translations": + query = query.order_by(Simulation.locale_count.desc(), Simulation.title) + elif sort == "updated": + query = query.order_by(Simulation.updated_date.desc(), Simulation.title) + else: + query = query.order_by(Simulation.title) + + total = query.count() + sims = query.offset((page - 1) * per_page).limit(per_page).all() + total_pages = max((total + per_page - 1) // per_page, 1) + + any_filter_active = bool( + subject or topic or grade or language or release or sort != "title" + ) + view_tab = view if view in ("browse", "filter", "customize") else "filter" + + customizable = [] + if view_tab == "customize": + customizable = ( + Simulation.query.filter_by(is_phet_studio=True) + .order_by(Simulation.title) + .all() + ) + + sims_by_subject = {} + if view_tab == "browse": + ordered = ['physics', 'math', 'chemistry', 'earth-science', 'biology'] + for slug in ordered: + sims_by_subject[slug] = ( + Simulation.query + .filter(Simulation.subjects_json.like(f'%"{slug}"%')) + .order_by(Simulation.title) + .limit(7) + .all() + ) + + return render_template( + "simulations.html", + sims=sims, + total=total, + page=page, + total_pages=total_pages, + subject=subject, + topic=topic, + topics=TOPICS_SEED, + customizable=customizable, + grade=grade, + language=language, + release=release, + sort=sort, + view_tab=view_tab, + languages=Language.query.order_by(Language.name).all(), + any_filter_active=any_filter_active, + sims_by_subject=sims_by_subject, + ) + + +@app.route("/simulations/category/") +def simulations_by_subject(slug): + subject = Subject.query.filter_by(slug=slug).first_or_404() + sims = ( + Simulation.query.filter(Simulation.subjects_json.like(f'%"{slug}"%')) + .order_by(Simulation.title) + .all() + ) + return render_template( + "category.html", subject=subject, sims=sims, + ) + + +@app.route("/simulation/") +def simulation_detail(slug): + # Read-only: GET must never mutate the DB. + sim = Simulation.query.filter_by(slug=slug).first_or_404() + + subjects_full = [ + s for s in Subject.query.filter(Subject.slug.in_(sim.subjects())).all() + ] + grades_full = [ + g for g in GradeLevel.query.filter(GradeLevel.slug.in_(sim.grades())).all() + ] + langs_full = [ + l for l in Language.query.filter(Language.code.in_(sim.languages())).all() + ] + + # Upstream publishes an explicit relatedSimulations list per sim; use it and + # keep its order. Fall back to same-subject titles only when it is empty. + related_slugs = sim.related() + related = [] + if related_slugs: + found = { + s.slug: s for s in + Simulation.query.filter(Simulation.slug.in_(related_slugs)).all() + } + related = [found[s] for s in related_slugs if s in found] + if not related: + related = ( + Simulation.query.filter(Simulation.id != sim.id) + .filter( + or_(*[ + Simulation.subjects_json.like(f'%"{s}"%') for s in sim.subjects() + ]) + ) + .order_by(Simulation.title) + .limit(6) + .all() + ) + activities = sim.activities.order_by(Activity.published_date.desc()).all() + + saved_row = ( + SavedSimulation.query.filter_by(user_id=current_user.id, sim_id=sim.id).first() + if current_user.is_authenticated + else None + ) + is_saved = saved_row is not None + saved_note = saved_row.notes if saved_row else "" + + return render_template( + "simulation_detail.html", + sim=sim, + subjects=subjects_full, + grades=grades_full, + languages=langs_full, + related=related, + activities=activities, + is_saved=is_saved, + saved_note=saved_note, + ) + + +@app.route("/search") +def search(): + q = request.args.get("q", "").strip() + sims = [] + if q: + # Match every whitespace-separated token independently so multi-word + # queries such as "build atom" still reach "Build an Atom". + tokens = [tok for tok in re.split(r"\W+", q) if tok] + query = Simulation.query + for tok in tokens or [q]: + pattern = f"%{tok}%" + query = query.filter( + or_( + Simulation.title.ilike(pattern), + Simulation.short_description.ilike(pattern), + Simulation.overview.ilike(pattern), + Simulation.topics_json.ilike(pattern), + ) + ) + sims = query.order_by(Simulation.title).all() + return render_template("search.html", query=q, sims=sims, total=len(sims)) + + +@app.route("/translations") +def translations(): + langs = ( + Language.query.order_by(Language.sim_count.desc(), Language.name) + .all() + ) + return render_template("translations.html", languages=langs) + + +@app.route("/translations/") +def translation_detail(code): + lang = Language.query.filter_by(code=code).first_or_404() + sims = ( + Simulation.query.filter(Simulation.languages_json.like(f'%"{code}"%')) + .order_by(Simulation.title) + .all() + ) + return render_template( + "translation_detail.html", language=lang, sims=sims, + ) + + +@app.route("/teachers") +def teachers(): + featured = ( + Activity.query.order_by(Activity.published_date.desc(), Activity.title) + .limit(6) + .all() + ) + return render_template("teachers.html", featured_activities=featured) + + +@app.route("/teachers/activities") +def activities(): + grade = request.args.get("grade", "") + query = Activity.query + if grade: + query = query.filter_by(grade_level=grade) + items = query.order_by(Activity.published_date.desc()).all() + return render_template("activities.html", activities=items, grade=grade) + + +@app.route("/teachers/activity/") +def activity_detail(activity_id): + activity = Activity.query.get_or_404(activity_id) + return render_template("activity_detail.html", activity=activity) + + +@app.route("/about") +def about(): + stats = { + "simulations": Simulation.query.count(), + "languages": Language.query.count(), + "subjects": Subject.query.count(), + "activities": Activity.query.count(), + } + return render_template("about.html", stats=stats) + + +@app.route("/accessibility") +def accessibility(): + return render_template("accessibility.html") + + +# --------------------------------------------------------------------------- +# Routes — auth +# --------------------------------------------------------------------------- + +EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$") + + +@app.route("/register", methods=["GET", "POST"]) +def register(): + if current_user.is_authenticated: + return redirect(url_for("account")) + if request.method == "POST": + email = request.form.get("email", "").strip().lower() + name = request.form.get("name", "").strip() + password = request.form.get("password", "") + institution = request.form.get("institution", "").strip() + country = request.form.get("country", "").strip() + + if not EMAIL_RE.match(email): + flash("Please enter a valid email address.", "error") + elif len(name) < 2: + flash("Please enter your full name.", "error") + elif len(password) < 8: + flash("Password must be at least 8 characters.", "error") + elif User.query.filter_by(email=email).first(): + flash("An account with that email already exists.", "error") + else: + user = User( + email=email, + name=name, + password_hash=bcrypt.generate_password_hash(password).decode(), + institution=institution, + country=country, + ) + db.session.add(user) + db.session.commit() + login_user(user) + flash("Welcome to PhET! Your account is ready.", "success") + return redirect(url_for("account")) + return render_template("register.html") + + +@app.route("/login", methods=["GET", "POST"]) +def login(): + if current_user.is_authenticated: + return redirect(url_for("account")) + if request.method == "POST": + email = request.form.get("email", "").strip().lower() + password = request.form.get("password", "") + user = User.query.filter_by(email=email).first() + if user and bcrypt.check_password_hash(user.password_hash, password): + login_user(user) + flash(f"Welcome back, {user.name}!", "success") + return redirect(request.args.get("next") or url_for("account")) + flash("Invalid email or password.", "error") + return render_template("login.html") + + +@app.route("/logout") +@login_required +def logout(): + logout_user() + flash("You have been signed out.", "success") + return redirect(url_for("index")) + + +@app.route("/account") +@login_required +def account(): + saved_rows = ( + SavedSimulation.query.filter_by(user_id=current_user.id) + .order_by(SavedSimulation.saved_at.desc()) + .all() + ) + return render_template("account.html", saved_rows=saved_rows) + + +@app.route("/api/save-sim", methods=["POST"]) +@login_required +def api_save_sim(): + data = request.get_json(silent=True) or request.form + sim_id = data.get("sim_id") + notes = (data.get("notes") or "").strip() + if not sim_id: + return jsonify({"ok": False, "error": "missing sim_id"}), 400 + sim = Simulation.query.get(int(sim_id)) + if not sim: + return jsonify({"ok": False, "error": "unknown simulation"}), 404 + existing = SavedSimulation.query.filter_by( + user_id=current_user.id, sim_id=sim.id, + ).first() + if existing: + existing.notes = notes or existing.notes + else: + db.session.add( + SavedSimulation( + user_id=current_user.id, sim_id=sim.id, notes=notes, + ) + ) + db.session.commit() + return jsonify({"ok": True, "saved": True, "sim_id": sim.id}) + + +@app.route("/api/unsave-sim", methods=["POST"]) +@login_required +def api_unsave_sim(): + data = request.get_json(silent=True) or request.form + sim_id = data.get("sim_id") + if not sim_id: + return jsonify({"ok": False, "error": "missing sim_id"}), 400 + row = SavedSimulation.query.filter_by( + user_id=current_user.id, sim_id=int(sim_id), + ).first() + if row: + db.session.delete(row) + db.session.commit() + return jsonify({"ok": True, "saved": False, "sim_id": int(sim_id)}) + + +# --------------------------------------------------------------------------- +# Error handlers +# --------------------------------------------------------------------------- + +@app.errorhandler(404) +def not_found(_): + return render_template("404.html"), 404 + + +# --------------------------------------------------------------------------- +# Seed data +# --------------------------------------------------------------------------- + +# Catalogue constants now come from catalog_data.py, which is harvested from +# phet.colorado.edu rather than generated. See that module's docstring. +SUBJECTS_SEED = catalog_data.SUBJECTS +TOPICS_SEED = catalog_data.TOPICS +GRADES_SEED = catalog_data.GRADES +LANGUAGES_SEED = catalog_data.LANGUAGES +SIMULATIONS_SEED = catalog_data.SIMULATIONS + +# Teacher lesson plans on the real site are submitted by named educators. This +# mirror does not reproduce them: the rows below are benchmark fixtures with +# synthetic authors, deliberately not attributed to real PhET contributors, and +# each one points at a simulation that genuinely exists upstream. +ACTIVITIES_SEED = [ + # (sim_slug, title, author, grade, duration_min, description) + ("forces-and-motion-basics", "Net Force Investigation", "R. Alvarez (benchmark fixture)", + "high", 40, "Students predict, observe and explain motion under balanced and " + "unbalanced forces using applied force and friction."), + ("build-an-atom", "Atomic Structure Lab", "M. Okafor (benchmark fixture)", + "middle", 60, "Build atoms of the first ten elements, identify subatomic " + "particles and explore how proton count determines the element."), + ("balancing-chemical-equations", "Coefficient Practice", "S. Lindqvist (benchmark fixture)", + "high", 60, "Balance equations of increasing difficulty and connect coefficients " + "to conservation of mass."), + ("natural-selection", "Selection Pressure Lab", "D. Ferreira (benchmark fixture)", + "high", 35, "Vary selection agents and mutations, then describe which traits " + "change survivability in each environment."), + ("energy-skate-park", "Conservation of Energy", "H. Nakamura (benchmark fixture)", + "high", 35, "Track kinetic, potential and thermal energy around a track and " + "explain conservation of mechanical energy."), + ("circuit-construction-kit-dc", "Series and Parallel", "T. Bagchi (benchmark fixture)", + "high", 55, "Build series and parallel circuits, measure current and voltage, " + "and compare the two topologies."), + ("gravity-and-orbits", "Modeling the Solar System", "L. Moreau (benchmark fixture)", + "middle", 50, "Relate the Sun, Earth, Moon and space station through gravity, " + "orbits and relative distance."), + ("states-of-matter", "Phase Change Inquiry", "P. Novak (benchmark fixture)", + "middle", 60, "Heat and cool substances and connect molecular motion to solid, " + "liquid and gas phases."), + ("ph-scale", "Acids and Bases in the Kitchen", "A. Haddad (benchmark fixture)", + "middle", 40, "Measure the pH of household liquids and relate the scale to " + "acid and base strength."), + ("graphing-lines", "Slope-Intercept Form", "C. Villanueva (benchmark fixture)", + "middle", 60, "Move between slope-intercept and point-slope form and predict " + "how each parameter moves the line."), + ("fractions-intro", "Equivalent Fractions Game", "J. Whitfield (benchmark fixture)", + "elementary", 50, "Build equivalent fractions with shapes and number lines, " + "then compare the results."), + ("density", "Identify the Mystery Block", "K. Solberg (benchmark fixture)", + "middle", 55, "Use mass and volume measurements to identify unknown blocks by " + "their density."), + ("wave-interference", "Boundary Identification", "N. Erdmann (benchmark fixture)", + "high", 40, "Compare reflection and transmission at boundaries for water, sound " + "and light waves."), + ("projectile-motion", "Launch Angle Study", "F. Castellano (benchmark fixture)", + "high", 50, "Vary launch angle, speed and drag, then predict range and flight " + "time for each configuration."), +] + + +# Benchmark-only accounts on a .test domain. These are fixtures for the +# environment, not mirrored upstream data. +BENCHMARK_USERS = [ + ("teacher@phet.test", "Ada Lovelace", "phet-teacher-pass", + "teacher", "Cherry Creek High School", "United States"), + ("student@phet.test", "Carl Sagan", "phet-student-pass", + "student", "Ithaca High School", "United States"), + ("research@phet.test", "Marie Curie", "phet-research-pass", + "researcher", "Sorbonne University", "France"), + ("demo@phet.test", "Demo User", "phet-demo-pass", + "teacher", "Demo School", "Canada"), +] + + +def seed_subjects(): + if Subject.query.count() > 0: + return + for slug, name, icon, color, desc in SUBJECTS_SEED: + db.session.add(Subject(slug=slug, name=name, icon=icon, + color=color, description=desc)) + db.session.commit() + + +def seed_grades(): + if GradeLevel.query.count() > 0: + return + for slug, name, age_range, sort_order in GRADES_SEED: + db.session.add(GradeLevel(slug=slug, name=name, age_range=age_range, + sort_order=sort_order)) + db.session.commit() + + +def seed_languages(): + if Language.query.count() > 0: + return + for row in LANGUAGES_SEED: + db.session.add(Language( + code=row["code"], name=row["name"], + native_name=row["native_name"], is_rtl=row["is_rtl"], + sim_count=0, + )) + db.session.commit() + + +def seed_simulations(): + if Simulation.query.count() > 0: + return + for row in SIMULATIONS_SEED: + db.session.add(Simulation( + slug=row["slug"], + title=row["title"], + short_description=row["description"][:300], + overview=row["description"], + learning_goals=row["learning_goals"], + subjects_json=json.dumps(row["subjects"]), + grades_json=json.dumps(row["grades"]), + # the filter facet ids plus the topic strip shown upstream + topics_json=json.dumps(row["topics"] + row["detail_topics"]), + languages_json=json.dumps(row["locales"]), + related_json=json.dumps(row["related"]), + version=row["version"], + is_html5=True, + # upstream has no editorial "featured" rail; the mirror marks the + # twelve simulations upstream flags as new so the homepage has a + # deterministic, source-backed selection. + is_featured=row["is_new"], + is_new=row["is_new"], + thumbnail=f"{row['slug']}.png", + release_date=date.fromisoformat(row["released"]), + updated_date=date.fromisoformat(row["updated"]), + locale_count=len(row["locales"]), + design_team=row["design_team"][:400], + has_teachers_guide=row["teachers_guide"], + is_phet_studio=row["is_phet_studio"], + upstream_url=row["upstream_page"], + )) + db.session.commit() + + counts = {} + for sim in Simulation.query.all(): + for code in sim.languages(): + counts[code] = counts.get(code, 0) + 1 + for lang in Language.query.all(): + lang.sim_count = counts.get(lang.code, 0) + db.session.commit() + + +def seed_activities(): + if Activity.query.count() > 0: + return + for sim_slug, title, author, grade, duration, description in ACTIVITIES_SEED: + sim = Simulation.query.filter_by(slug=sim_slug).first() + if sim is None: + continue + db.session.add(Activity( + sim_id=sim.id, title=title, author=author, + grade_level=grade, duration_min=duration, description=description, + file_type="PDF", published_date=sim.updated_date, + )) + db.session.commit() + + +def seed_benchmark_users(): + if User.query.count() > 0: + return + for email, name, password, role, institution, country in BENCHMARK_USERS: + db.session.add(User( + email=email, + name=name, + password_hash=bcrypt.generate_password_hash(password).decode(), + role=role, + institution=institution, + country=country, + )) + db.session.commit() + + # Pre-populate saved sims for the demo teacher so the account page is + # non-empty when an agent inspects it after login. + teacher = User.query.filter_by(email="teacher@phet.test").first() + if teacher: + for slug in ("forces-and-motion-basics", "build-an-atom", + "ph-scale", "natural-selection"): + sim = Simulation.query.filter_by(slug=slug).first() + if sim: + db.session.add(SavedSimulation( + user_id=teacher.id, sim_id=sim.id, + notes=f"Use for {sim.title} unit opener.", + )) + db.session.commit() + + +def seed_all(): + seed_subjects() + seed_grades() + seed_languages() + seed_simulations() + seed_activities() + seed_benchmark_users() + + +with app.app_context(): + db.create_all() + seed_all() + + +if __name__ == "__main__": + port = int(os.environ.get("PORT", 40029)) + app.run(host="0.0.0.0", port=port, debug=False) diff --git a/sites/phet_simulations/catalog_data.py b/sites/phet_simulations/catalog_data.py new file mode 100644 index 00000000..3b852b55 --- /dev/null +++ b/sites/phet_simulations/catalog_data.py @@ -0,0 +1,13616 @@ +"""Real PhET catalogue data for the WebHarbor mirror. + +Every row here is harvested from phet.colorado.edu, not generated: + +* simulation slug, title, description and sample learning goals come from the + official metadata service + ``/services/metadata/1.3/simulations?format=json&type=html&locale=en``; +* subject and topic membership come from the numeric ``subjects`` ids in that + feed, whose meaning was confirmed by intersecting each id against the live + filter facets (Physics 65, Math & Statistics 51, Chemistry 35, Earth & Space + 19, Biology 8 -- exact matches on all four checked); +* grade bands come from ``lowGradeLevel``/``highGradeLevel`` (0 elementary, + 1 middle, 2 high, 3 university); +* version strings, release dates and last-updated dates come from the project + ``version.string``, ``createTime`` and ``updateTime``; +* the per-simulation locale list comes from ``localizedSimulations``; +* ``detail_topics`` are the topic strip shown on each upstream simulation page; +* ``related`` are the upstream ``relatedSimulations`` ids resolved to slugs; +* thumbnails are the official 600px screenshots. + +Facts PhET does not publish -- play counts, download counts and a per-simulation +runtime -- are deliberately absent rather than invented. Harvested 2026-09-13. +""" + +SUBJECTS = [ + ("physics", "Physics", "physics.svg", "#f8d7e3", "Motion, forces, energy, waves, light and quantum phenomena."), + ("math", "Math & Statistics", "math.svg", "#fdeec8", "Number, algebra, geometry, data and statistics."), + ("chemistry", "Chemistry", "chemistry.svg", "#cfeaf5", "Atoms, molecules, reactions, solutions and quantum chemistry."), + ("earth-science", "Earth & Space", "earth.svg", "#d5ecd5", "Earth systems, astronomy and space science."), + ("biology", "Biology", "biology.svg", "#e6d8f0", "Cells, genetics, neurons and natural selection."), +] + +# Upstream groups its subject facet into these sub-topics. +TOPICS = [ + ("motion", "Motion", "physics"), + ("sound-and-waves", "Sound & Waves", "physics"), + ("work-energy-and-power", "Work, Energy & Power", "physics"), + ("heat-and-thermo", "Heat & Thermo", "physics"), + ("quantum-phenomena", "Quantum Phenomena", "physics"), + ("light-and-radiation", "Light & Radiation", "physics"), + ("electricity-magnets-and-circuits", "Electricity, Magnets & Circuits", "physics"), + ("math-concepts", "Math Concepts", "math"), + ("math-applications", "Math Applications", "math"), + ("general-chemistry", "General Chemistry", "chemistry"), + ("quantum-chemistry", "Quantum Chemistry", "chemistry"), +] + +GRADES = [ + ("elementary", "Elementary School", "Ages 5-10", 1), + ("middle", "Middle School", "Ages 11-13", 2), + ("high", "High School", "Ages 14-18", 3), + ("university", "University", "Ages 18+", 4), +] + +LANGUAGES = [ { 'code': 'en', + 'name': 'English', + 'native_name': 'English', + 'is_rtl': False, + 'upstream_html5_count': None, + 'seeded_count': 120}, + { 'code': 'af', + 'name': 'Afrikaans', + 'native_name': 'Afrikaans', + 'is_rtl': False, + 'upstream_html5_count': 47, + 'seeded_count': 47}, + { 'code': 'ak', + 'name': 'Akan', + 'native_name': 'Akan', + 'is_rtl': False, + 'upstream_html5_count': 2, + 'seeded_count': 2}, + { 'code': 'sq', + 'name': 'Albanian', + 'native_name': 'Shqip', + 'is_rtl': False, + 'upstream_html5_count': 101, + 'seeded_count': 100}, + { 'code': 'am', + 'name': 'Amharic', + 'native_name': 'አማርኛ', + 'is_rtl': False, + 'upstream_html5_count': 106, + 'seeded_count': 105}, + { 'code': 'ar', + 'name': 'Arabic', + 'native_name': 'العربية', + 'is_rtl': True, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'ar_KW', + 'name': 'Arabic (Kuwait)', + 'native_name': 'العربية (الكويت)', + 'is_rtl': True, + 'upstream_html5_count': 3, + 'seeded_count': 3}, + { 'code': 'ar_LY', + 'name': 'Arabic (Libya)', + 'native_name': 'العربية (ليبيا)', + 'is_rtl': True, + 'upstream_html5_count': 28, + 'seeded_count': 27}, + { 'code': 'ar_MA', + 'name': 'Arabic (Morocco)', + 'native_name': 'العربية (المغرب)', + 'is_rtl': True, + 'upstream_html5_count': 105, + 'seeded_count': 103}, + { 'code': 'ar_SA', + 'name': 'Arabic (Saudi Arabia)', + 'native_name': 'العربية (السعودية)', + 'is_rtl': True, + 'upstream_html5_count': 56, + 'seeded_count': 56}, + { 'code': 'ar_SY', + 'name': 'Arabic (Syria)', + 'native_name': 'العربية (سوريا)', + 'is_rtl': True, + 'upstream_html5_count': 1, + 'seeded_count': 1}, + { 'code': 'hy', + 'name': 'Armenian', + 'native_name': 'Հայերեն', + 'is_rtl': False, + 'upstream_html5_count': 61, + 'seeded_count': 61}, + { 'code': 'ay', + 'name': 'Aymara', + 'native_name': 'aymar aru', + 'is_rtl': False, + 'upstream_html5_count': 1, + 'seeded_count': 1}, + { 'code': 'az', + 'name': 'Azerbaijani', + 'native_name': 'azərbaycan dili', + 'is_rtl': False, + 'upstream_html5_count': 107, + 'seeded_count': 106}, + { 'code': 'ba', + 'name': 'Bashkir', + 'native_name': 'башҡорт теле', + 'is_rtl': False, + 'upstream_html5_count': 0, + 'seeded_count': 0}, + { 'code': 'eu', + 'name': 'Basque', + 'native_name': 'euskara', + 'is_rtl': False, + 'upstream_html5_count': 106, + 'seeded_count': 105}, + { 'code': 'be', + 'name': 'Belarusian', + 'native_name': 'беларуская мова', + 'is_rtl': False, + 'upstream_html5_count': 57, + 'seeded_count': 56}, + { 'code': 'bn', + 'name': 'Bengali', + 'native_name': 'বাংলা', + 'is_rtl': False, + 'upstream_html5_count': 104, + 'seeded_count': 103}, + { 'code': 'bi', + 'name': 'Bislama', + 'native_name': 'Bislama', + 'is_rtl': False, + 'upstream_html5_count': 1, + 'seeded_count': 1}, + { 'code': 'bs', + 'name': 'Bosnian', + 'native_name': 'bosanski jezik', + 'is_rtl': False, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'bg', + 'name': 'Bulgarian', + 'native_name': 'български език', + 'is_rtl': False, + 'upstream_html5_count': 105, + 'seeded_count': 104}, + { 'code': 'my', + 'name': 'Burmese', + 'native_name': 'ဗမာစာ', + 'is_rtl': False, + 'upstream_html5_count': 13, + 'seeded_count': 13}, + { 'code': 'ca', + 'name': 'Catalan', + 'native_name': 'Català', + 'is_rtl': False, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'ny', + 'name': 'Chichewa', + 'native_name': 'Nyanja', + 'is_rtl': False, + 'upstream_html5_count': 105, + 'seeded_count': 104}, + { 'code': 'zh_HK', + 'name': 'Chinese (Hong Kong)', + 'native_name': '中文 (香港)', + 'is_rtl': False, + 'upstream_html5_count': 35, + 'seeded_count': 35}, + { 'code': 'zh_CN', + 'name': 'Chinese (Simplified)', + 'native_name': '中文 (中国)', + 'is_rtl': False, + 'upstream_html5_count': 98, + 'seeded_count': 98}, + { 'code': 'zh_TW', + 'name': 'Chinese (Traditional)', + 'native_name': '中文 (台灣)', + 'is_rtl': False, + 'upstream_html5_count': 116, + 'seeded_count': 114}, + { 'code': 'hr', + 'name': 'Croatian', + 'native_name': 'Hrvatski', + 'is_rtl': False, + 'upstream_html5_count': 108, + 'seeded_count': 106}, + { 'code': 'cs', + 'name': 'Czech', + 'native_name': 'čeština', + 'is_rtl': False, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'da', + 'name': 'Danish', + 'native_name': 'dansk', + 'is_rtl': False, + 'upstream_html5_count': 122, + 'seeded_count': 120}, + { 'code': 'nl', + 'name': 'Dutch', + 'native_name': 'Nederlands', + 'is_rtl': False, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'ef', + 'name': 'Efik', + 'native_name': 'Usem Efịk', + 'is_rtl': False, + 'upstream_html5_count': 1, + 'seeded_count': 1}, + { 'code': 'en_CA', + 'name': 'English (Canada)', + 'native_name': 'English (Canada)', + 'is_rtl': False, + 'upstream_html5_count': 1, + 'seeded_count': 1}, + { 'code': 'en_GB', + 'name': 'English (United Kingdom)', + 'native_name': 'English (United Kingdom)', + 'is_rtl': False, + 'upstream_html5_count': 2, + 'seeded_count': 2}, + { 'code': 'et', + 'name': 'Estonian', + 'native_name': 'eesti', + 'is_rtl': False, + 'upstream_html5_count': 35, + 'seeded_count': 35}, + { 'code': 'ee', + 'name': 'Ewe', + 'native_name': 'Eʋegbe', + 'is_rtl': False, + 'upstream_html5_count': 3, + 'seeded_count': 3}, + { 'code': 'fo', + 'name': 'Faroese', + 'native_name': 'føroyskt', + 'is_rtl': False, + 'upstream_html5_count': 27, + 'seeded_count': 27}, + { 'code': 'fi', + 'name': 'Finnish', + 'native_name': 'suomi', + 'is_rtl': False, + 'upstream_html5_count': 94, + 'seeded_count': 93}, + { 'code': 'fr', + 'name': 'French', + 'native_name': 'français', + 'is_rtl': False, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'fu', + 'name': 'Friulian', + 'native_name': 'furlan', + 'is_rtl': False, + 'upstream_html5_count': 0, + 'seeded_count': 0}, + { 'code': 'gl', + 'name': 'Galician', + 'native_name': 'galego', + 'is_rtl': False, + 'upstream_html5_count': 35, + 'seeded_count': 35}, + { 'code': 'lg', + 'name': 'Ganda', + 'native_name': 'Luganda', + 'is_rtl': False, + 'upstream_html5_count': 103, + 'seeded_count': 102}, + { 'code': 'ka', + 'name': 'Georgian', + 'native_name': 'ქართული ენა', + 'is_rtl': False, + 'upstream_html5_count': 63, + 'seeded_count': 63}, + { 'code': 'de', + 'name': 'German', + 'native_name': 'Deutsch', + 'is_rtl': False, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'el', + 'name': 'Greek', + 'native_name': 'Ελληνικά', + 'is_rtl': False, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'gu', + 'name': 'Gujarati', + 'native_name': 'ગુજરાતી', + 'is_rtl': False, + 'upstream_html5_count': 117, + 'seeded_count': 115}, + { 'code': 'ht', + 'name': 'Haitian', + 'native_name': 'Kreyòl ayisyen', + 'is_rtl': False, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'ha', + 'name': 'Hausa', + 'native_name': 'Hausa', + 'is_rtl': False, + 'upstream_html5_count': 116, + 'seeded_count': 114}, + { 'code': 'iw', + 'name': 'Hebrew', + 'native_name': 'עברית', + 'is_rtl': True, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'hi', + 'name': 'Hindi', + 'native_name': 'हिन्दी', + 'is_rtl': False, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'hu', + 'name': 'Hungarian', + 'native_name': 'magyar', + 'is_rtl': False, + 'upstream_html5_count': 104, + 'seeded_count': 104}, + { 'code': 'is', + 'name': 'Icelandic', + 'native_name': 'Íslenska', + 'is_rtl': False, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'ig', + 'name': 'Igbo', + 'native_name': 'Asụsụ Igbo', + 'is_rtl': False, + 'upstream_html5_count': 104, + 'seeded_count': 103}, + { 'code': 'in', + 'name': 'Indonesian', + 'native_name': 'Indonesia', + 'is_rtl': False, + 'upstream_html5_count': 37, + 'seeded_count': 37}, + { 'code': 'ga', + 'name': 'Irish', + 'native_name': 'Gaeilge', + 'is_rtl': False, + 'upstream_html5_count': 53, + 'seeded_count': 53}, + { 'code': 'it', + 'name': 'Italian', + 'native_name': 'italiano', + 'is_rtl': False, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'ja', + 'name': 'Japanese', + 'native_name': '日本語', + 'is_rtl': False, + 'upstream_html5_count': 102, + 'seeded_count': 102}, + { 'code': 'kn', + 'name': 'Kannada', + 'native_name': 'ಕನ್ನಡ', + 'is_rtl': False, + 'upstream_html5_count': 70, + 'seeded_count': 70}, + { 'code': 'kk', + 'name': 'Kazakh', + 'native_name': 'қазақ тілі', + 'is_rtl': False, + 'upstream_html5_count': 100, + 'seeded_count': 99}, + { 'code': 'km', + 'name': 'Khmer', + 'native_name': 'ខេមរភាសា', + 'is_rtl': False, + 'upstream_html5_count': 45, + 'seeded_count': 44}, + { 'code': 'ki', + 'name': 'Kikuyu', + 'native_name': 'Gĩkũyũ', + 'is_rtl': False, + 'upstream_html5_count': 81, + 'seeded_count': 80}, + { 'code': 'rw', + 'name': 'Kinyarwanda', + 'native_name': 'Ikinyarwanda', + 'is_rtl': False, + 'upstream_html5_count': 102, + 'seeded_count': 101}, + { 'code': 'ko', + 'name': 'Korean', + 'native_name': '한국어', + 'is_rtl': False, + 'upstream_html5_count': 119, + 'seeded_count': 117}, + { 'code': 'ku_TR', + 'name': 'Kurdish (Kurmanji)', + 'native_name': 'Kurmancî', + 'is_rtl': True, + 'upstream_html5_count': 4, + 'seeded_count': 4}, + { 'code': 'ku', + 'name': 'Kurdish (Sorani)', + 'native_name': 'سۆرانی', + 'is_rtl': True, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'lo', + 'name': 'Lao', + 'native_name': 'ພາສາລາວ', + 'is_rtl': False, + 'upstream_html5_count': 45, + 'seeded_count': 45}, + { 'code': 'lv', + 'name': 'Latvian', + 'native_name': 'latviešu valoda', + 'is_rtl': False, + 'upstream_html5_count': 91, + 'seeded_count': 91}, + { 'code': 'lt', + 'name': 'Lithuanian', + 'native_name': 'lietuvių kalba', + 'is_rtl': False, + 'upstream_html5_count': 78, + 'seeded_count': 77}, + { 'code': 'mk', + 'name': 'Macedonian', + 'native_name': 'македонски јазик', + 'is_rtl': False, + 'upstream_html5_count': 29, + 'seeded_count': 29}, + { 'code': 'mg', + 'name': 'Malagasy', + 'native_name': 'malagasy', + 'is_rtl': False, + 'upstream_html5_count': 31, + 'seeded_count': 31}, + { 'code': 'ms', + 'name': 'Malay', + 'native_name': 'Bahasa Malaysia', + 'is_rtl': False, + 'upstream_html5_count': 117, + 'seeded_count': 115}, + { 'code': 'ml', + 'name': 'Malayalam', + 'native_name': 'മലയാളം', + 'is_rtl': False, + 'upstream_html5_count': 8, + 'seeded_count': 8}, + { 'code': 'mt', + 'name': 'Maltese', + 'native_name': 'Malti', + 'is_rtl': False, + 'upstream_html5_count': 97, + 'seeded_count': 96}, + { 'code': 'mi', + 'name': 'Maori', + 'native_name': 'reo Māori', + 'is_rtl': False, + 'upstream_html5_count': 72, + 'seeded_count': 72}, + { 'code': 'mr', + 'name': 'Marathi', + 'native_name': 'मराठी', + 'is_rtl': False, + 'upstream_html5_count': 88, + 'seeded_count': 87}, + { 'code': 'mo', + 'name': 'Moldavian', + 'native_name': 'Moldavian', + 'is_rtl': False, + 'upstream_html5_count': 1, + 'seeded_count': 1}, + { 'code': 'mn', + 'name': 'Mongolian', + 'native_name': 'Монгол хэл', + 'is_rtl': False, + 'upstream_html5_count': 120, + 'seeded_count': 118}, + { 'code': 'ne', + 'name': 'Nepali', + 'native_name': 'नेपाली', + 'is_rtl': False, + 'upstream_html5_count': 5, + 'seeded_count': 5}, + { 'code': 'se', + 'name': 'Northern Sami', + 'native_name': 'Davvisámegiella', + 'is_rtl': False, + 'upstream_html5_count': 8, + 'seeded_count': 8}, + { 'code': 'nb', + 'name': 'Norwegian Bokmal', + 'native_name': 'Norwegian Bokmål', + 'is_rtl': False, + 'upstream_html5_count': 55, + 'seeded_count': 55}, + { 'code': 'nn', + 'name': 'Norwegian Nynorsk', + 'native_name': 'Norsk nynorsk', + 'is_rtl': False, + 'upstream_html5_count': 31, + 'seeded_count': 31}, + { 'code': 'ua', + 'name': 'Náhuatl', + 'native_name': 'Náhuatl', + 'is_rtl': False, + 'upstream_html5_count': 1, + 'seeded_count': 1}, + { 'code': 'oc', + 'name': 'Occitan', + 'native_name': 'occitan', + 'is_rtl': False, + 'upstream_html5_count': 1, + 'seeded_count': 1}, + { 'code': 'or', + 'name': 'Oriya', + 'native_name': 'ଓଡ଼ିଆ', + 'is_rtl': False, + 'upstream_html5_count': 14, + 'seeded_count': 14}, + { 'code': 'om', + 'name': 'Oromo', + 'native_name': 'Afaan Oromoo', + 'is_rtl': False, + 'upstream_html5_count': 112, + 'seeded_count': 110}, + { 'code': 'ps', + 'name': 'Pashto', + 'native_name': 'پښتو', + 'is_rtl': True, + 'upstream_html5_count': 107, + 'seeded_count': 106}, + { 'code': 'fa', + 'name': 'Persian', + 'native_name': 'فارسی', + 'is_rtl': True, + 'upstream_html5_count': 120, + 'seeded_count': 118}, + { 'code': 'fa_DA', + 'name': 'Persian (Dari)', + 'native_name': 'دری', + 'is_rtl': True, + 'upstream_html5_count': 103, + 'seeded_count': 102}, + { 'code': 'pl', + 'name': 'Polish', + 'native_name': 'Polski', + 'is_rtl': False, + 'upstream_html5_count': 122, + 'seeded_count': 120}, + { 'code': 'pt', + 'name': 'Portuguese', + 'native_name': 'Português', + 'is_rtl': False, + 'upstream_html5_count': 92, + 'seeded_count': 91}, + { 'code': 'pt_BR', + 'name': 'Portuguese (Brazil)', + 'native_name': 'português (Brasil)', + 'is_rtl': False, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'pr', + 'name': 'Purépecha', + 'native_name': 'Pʼurhépecha', + 'is_rtl': False, + 'upstream_html5_count': 11, + 'seeded_count': 11}, + { 'code': 'qu', + 'name': 'Quechua', + 'native_name': 'Qhichwa', + 'is_rtl': False, + 'upstream_html5_count': 3, + 'seeded_count': 3}, + { 'code': 'ro', + 'name': 'Romanian', + 'native_name': 'Română', + 'is_rtl': False, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'ru', + 'name': 'Russian', + 'native_name': 'Русский', + 'is_rtl': False, + 'upstream_html5_count': 91, + 'seeded_count': 90}, + { 'code': 'sp', + 'name': 'Sepedi', + 'native_name': 'Sesotho sa Leboa', + 'is_rtl': False, + 'upstream_html5_count': 27, + 'seeded_count': 27}, + { 'code': 'sr', + 'name': 'Serbian', + 'native_name': 'српски језик', + 'is_rtl': False, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'sh', + 'name': 'Serbo-Croatian', + 'native_name': 'Serbo-Croatian', + 'is_rtl': False, + 'upstream_html5_count': 1, + 'seeded_count': 1}, + { 'code': 'sn', + 'name': 'Shona', + 'native_name': 'chiShona', + 'is_rtl': False, + 'upstream_html5_count': 32, + 'seeded_count': 32}, + { 'code': 'sd', + 'name': 'Sindhi', + 'native_name': 'سنڌي', + 'is_rtl': True, + 'upstream_html5_count': 7, + 'seeded_count': 7}, + { 'code': 'si', + 'name': 'Sinhalese', + 'native_name': 'සිංහල', + 'is_rtl': False, + 'upstream_html5_count': 40, + 'seeded_count': 40}, + { 'code': 'sk', + 'name': 'Slovak', + 'native_name': 'slovenčina', + 'is_rtl': False, + 'upstream_html5_count': 111, + 'seeded_count': 109}, + { 'code': 'sl', + 'name': 'Slovenian', + 'native_name': 'slovenščina', + 'is_rtl': False, + 'upstream_html5_count': 107, + 'seeded_count': 105}, + { 'code': 'st', + 'name': 'Sotho', + 'native_name': 'Sesotho', + 'is_rtl': False, + 'upstream_html5_count': 107, + 'seeded_count': 106}, + { 'code': 'es', + 'name': 'Spanish', + 'native_name': 'español', + 'is_rtl': False, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'es_CO', + 'name': 'Spanish (Colombia)', + 'native_name': 'español (Colombia)', + 'is_rtl': False, + 'upstream_html5_count': 3, + 'seeded_count': 3}, + { 'code': 'es_MX', + 'name': 'Spanish (Mexico)', + 'native_name': 'español (México)', + 'is_rtl': False, + 'upstream_html5_count': 85, + 'seeded_count': 85}, + { 'code': 'es_PE', + 'name': 'Spanish (Peru', + 'native_name': 'español (Perú)', + 'is_rtl': False, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'es_ES', + 'name': 'Spanish (Spain)', + 'native_name': 'español (España)', + 'is_rtl': False, + 'upstream_html5_count': 34, + 'seeded_count': 34}, + { 'code': 'es_UY', + 'name': 'Spanish (Uruguay)', + 'native_name': 'español (Uruguay)', + 'is_rtl': False, + 'upstream_html5_count': 1, + 'seeded_count': 1}, + { 'code': 'sw', + 'name': 'Swahili', + 'native_name': 'Kiswahili', + 'is_rtl': False, + 'upstream_html5_count': 117, + 'seeded_count': 115}, + { 'code': 'sv', + 'name': 'Swedish', + 'native_name': 'Svenska', + 'is_rtl': False, + 'upstream_html5_count': 113, + 'seeded_count': 111}, + { 'code': 'tl', + 'name': 'Tagalog', + 'native_name': 'Wikang Tagalog', + 'is_rtl': False, + 'upstream_html5_count': 3, + 'seeded_count': 3}, + { 'code': 'tg', + 'name': 'Tajik', + 'native_name': 'тоҷикӣ', + 'is_rtl': False, + 'upstream_html5_count': 44, + 'seeded_count': 44}, + { 'code': 'ta', + 'name': 'Tamil', + 'native_name': 'தமிழ்', + 'is_rtl': False, + 'upstream_html5_count': 88, + 'seeded_count': 87}, + { 'code': 'te', + 'name': 'Telugu', + 'native_name': 'తెలుగు', + 'is_rtl': False, + 'upstream_html5_count': 120, + 'seeded_count': 118}, + { 'code': 'th', + 'name': 'Thai', + 'native_name': 'ไทย', + 'is_rtl': False, + 'upstream_html5_count': 87, + 'seeded_count': 86}, + { 'code': 'bo', + 'name': 'Tibetan', + 'native_name': 'བོད་ཡིག', + 'is_rtl': False, + 'upstream_html5_count': 23, + 'seeded_count': 23}, + { 'code': 'ti', + 'name': 'Tigrinya', + 'native_name': 'ትግርኛ', + 'is_rtl': False, + 'upstream_html5_count': 8, + 'seeded_count': 8}, + { 'code': 'tn', + 'name': 'Tswana', + 'native_name': 'Setswana', + 'is_rtl': False, + 'upstream_html5_count': 26, + 'seeded_count': 26}, + { 'code': 'tr', + 'name': 'Turkish', + 'native_name': 'Türkçe', + 'is_rtl': False, + 'upstream_html5_count': 117, + 'seeded_count': 115}, + { 'code': 'tk', + 'name': 'Turkmen', + 'native_name': 'Türkmen', + 'is_rtl': False, + 'upstream_html5_count': 100, + 'seeded_count': 99}, + { 'code': 'tw', + 'name': 'Twi', + 'native_name': 'Twi', + 'is_rtl': False, + 'upstream_html5_count': 101, + 'seeded_count': 100}, + { 'code': 'uk', + 'name': 'Ukrainian', + 'native_name': 'Українська', + 'is_rtl': False, + 'upstream_html5_count': 97, + 'seeded_count': 95}, + { 'code': 'ur', + 'name': 'Urdu', + 'native_name': 'اردو', + 'is_rtl': True, + 'upstream_html5_count': 43, + 'seeded_count': 43}, + { 'code': 'uz', + 'name': 'Uzbek', + 'native_name': "O'zbekcha", + 'is_rtl': False, + 'upstream_html5_count': 110, + 'seeded_count': 108}, + { 'code': 'vi', + 'name': 'Vietnamese', + 'native_name': 'Tiếng Việt', + 'is_rtl': False, + 'upstream_html5_count': 121, + 'seeded_count': 119}, + { 'code': 'cy', + 'name': 'Welsh', + 'native_name': 'Cymraeg', + 'is_rtl': False, + 'upstream_html5_count': 44, + 'seeded_count': 44}, + { 'code': 'wo', + 'name': 'Wolof', + 'native_name': 'Wollof', + 'is_rtl': False, + 'upstream_html5_count': 5, + 'seeded_count': 5}, + { 'code': 'xh', + 'name': 'Xhosa', + 'native_name': 'isiXhosa', + 'is_rtl': False, + 'upstream_html5_count': 6, + 'seeded_count': 6}, + { 'code': 'yo', + 'name': 'Yoruba', + 'native_name': 'Yorùbá', + 'is_rtl': False, + 'upstream_html5_count': 115, + 'seeded_count': 113}, + { 'code': 'zu', + 'name': 'Zulu', + 'native_name': 'isiZulu', + 'is_rtl': False, + 'upstream_html5_count': 108, + 'seeded_count': 106}] + +SIMULATIONS = [ { 'slug': 'acid-base-solutions', + 'is_phet_studio': True, + 'title': 'Acid-Base Solutions', + 'description': 'How do strong and weak acids differ? Use lab tools on your computer to ' + 'find out! Dip the paper or the probe into solution to measure the pH, or ' + 'put in the electrodes to measure the conductivity. Then see how ' + 'concentration and strength affect pH. Can a weak acid solution have the ' + 'same pH as a strong acid solution?', + 'learning_goals': 'Given acids or bases at the same concentration, demonstrate ' + 'understanding of acid and base strength by: 1.Relating the strength of ' + 'an acid or base to the extent to which it dissociates in water ' + '2.Identifying all of the molecules and ions that are present in a given ' + 'acid or base solution. 3.Comparing the relative concentrations of ' + 'molecules and ions in weak versus strong acid (or base) solutions. ' + '4.Describing the similarities and differences between strong acids and ' + 'weak acids or strong bases and weak bases.
Demonstrate ' + 'understanding of solution concentration by: 1.Describing the ' + 'similarities and differences between concentrated and dilute solutions. ' + '2.Comparing the concentrations of all molecules and ions in ' + 'concentrated versus dilute solutions of a particular acid or ' + 'base.
Use both the strength of the acid or base and the ' + 'concentration of its solution in order to: 1.Describe in words and ' + 'pictures (graphs or molecular drawings) what it means if you have a: ' + 'Concentrated solution of a weak acid (or base) or Concentrated solution ' + 'of a strong acid (or base) or other combinations. 2.Investigate ' + 'different combinations of strength/concentrations that result in same ' + 'pH values.
Describe how common tools (pH meter, conductivity, pH ' + 'paper) help identify whether a solution is an acid or base and strong ' + 'or weak and concentrated or dilute.', + 'subjects': ['chemistry'], + 'topics': ['general-chemistry'], + 'detail_topics': ['Solutions', 'Acids', 'Bases'], + 'grades': ['high', 'university'], + 'version': '1.3.15', + 'released': '2014-04-18', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mg', + 'mi', + 'mk', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'or', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sd', + 'si', + 'sk', + 'sl', + 'sn', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['ph-scale-basics', 'ph-scale'], + 'design_team': 'Kelly Lancaster (lead); Chris Malley (developer); Bryce Gruneich; Patricia ' + 'Loeblein; Emily B. Moore; Robert Parson; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/acid-base-solutions', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/acid-base-solutions/latest/acid-base-solutions-600.png'}, + { 'slug': 'area-builder', + 'is_phet_studio': False, + 'title': 'Area Builder', + 'description': 'Create your own shapes using colorful blocks and explore the relationship ' + 'between perimeter and area. Compare the area and perimeter of two shapes ' + 'side-by-side. Challenge yourself in the game screen to build shapes or ' + 'find the area of funky figures. Try to collect lots of stars!', + 'learning_goals': 'Find the area of a shape by counting unit squares
Describe the ' + 'relationship between area and perimeter
Build shapes with a given ' + 'area and/or perimeter
Find the area of an irregular shape by ' + 'decomposing into smaller, regular shapes (e.g., rectangles, triangles, ' + 'squares)
Determine the scale factor of similar ' + 'shapes
Generalize how area and perimeter change when scaling shapes', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Area', 'Perimeter', 'Fractional areas'], + 'grades': ['elementary', 'middle'], + 'version': '1.1.38', + 'released': '2014-10-11', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mg', + 'mi', + 'ml', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'nn', + 'ny', + 'oc', + 'om', + 'pl', + 'pr', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'wo', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['arithmetic', 'area-model-introduction', 'quadrilateral'], + 'design_team': 'Karina Hensberry (lead); John Blanco (developer); Bryce Gruneich ; Amanda ' + 'McGarry ; Ariel Paul ; Kathy Perkins ; Beth Stade', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/area-builder', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/area-builder/latest/area-builder-600.png'}, + { 'slug': 'area-model-algebra', + 'is_phet_studio': False, + 'title': 'Area Model Algebra', + 'description': 'Build rectangles of various sizes and relate multiplication to area. ' + 'Discover new strategies for multiplying algebraic expressions. Use the ' + 'game screen to test your multiplication and factoring skills!', + 'learning_goals': 'Develop and justify a method to use the area model to determine the ' + 'product of a monomial and a binomial or the product of two ' + 'binomials.
Factor an expression, including expressions containing a ' + 'variable.
Recognize that area represents the product of two numbers ' + 'and is additive.
Represent a multiplication problem as the area of ' + 'a rectangle, proportionally or using generic area.
Develop and ' + 'justify a strategy to determine the product of two multi-digit numbers ' + 'by representing the product as an area or the sum of areas.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Polynomials', 'Products', 'Factors'], + 'grades': ['middle', 'high'], + 'version': '1.3.9', + 'released': '2018-04-25', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lv', + 'mg', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'wo', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['expression-exchange', 'area-model-multiplication'], + 'design_team': 'Amy Hanson (lead designer) ; Amanda McGarry (co-lead) ; Jonathan Olson ' + '(developer) ; Karina Hensberry; Susan Miller; Ariel Paul; Kathy Perkins; ' + 'Mariah Hermsmeyer (artwork); Diana López Tavares (artwork)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/area-model-algebra', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/area-model-algebra/latest/area-model-algebra-600.png'}, + { 'slug': 'area-model-decimals', + 'is_phet_studio': False, + 'title': 'Area Model Decimals', + 'description': 'Build rectangles of various sizes and relate multiplication to area. ' + 'Partition a rectangle into smaller areas and discover new strategies for ' + 'multiplying decimals!', + 'learning_goals': 'Recognize that area represents the product of two numbers and is ' + 'additive.
Develop and justify a strategy that uses the area model ' + 'to simplify a decimal multiplication problem.
Devise a strategy to ' + 'determine the product of two rational numbers (or an integer and a ' + 'rational number) by representing the product as an area or the sum of ' + 'areas.
Use an area model to understand why multiplying a given ' + 'number by a decimal less than 1 results in a product smaller than the ' + 'given number.
Interpret the product of two rational numbers using ' + 'base-ten tiles', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Multiplication', 'Factors', 'Area Model'], + 'grades': ['elementary', 'middle'], + 'version': '1.3.8', + 'released': '2018-04-25', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mg', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'qu', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['area-model-multiplication'], + 'design_team': 'Amy Hanson (lead designer) ; Amanda McGarry (co-lead) ; Jonathan Olson ' + '(developer) ; Karina Hensberry; Susan Miller; Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/area-model-decimals', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/area-model-decimals/latest/area-model-decimals-600.png'}, + { 'slug': 'area-model-introduction', + 'is_phet_studio': False, + 'title': 'Area Model Introduction', + 'description': 'Build rectangles of various sizes and relate multiplication to area. ' + 'Partition a rectangle into two areas to discover the distributive ' + 'property.', + 'learning_goals': 'Recognize that area represents the product of two numbers.
Develop ' + 'and justify a strategy that uses the area model to simplify a ' + 'multiplication problem.
Represent a multiplication problem as the ' + 'proportional area of a rectangle.
Looks for patterns in the total ' + 'area calculation.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Multiplication', 'Products', 'Factors'], + 'grades': ['elementary'], + 'version': '1.3.8', + 'released': '2018-04-25', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'ar_SA', + 'ay', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lv', + 'mg', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'tg', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['area-builder', 'arithmetic', 'area-model-multiplication'], + 'design_team': 'Amy Hanson (lead designer) ; Amanda McGarry (co-lead) ; Jonathan Olson ' + '(developer) ; Karina Hensberry; Susan Miller; Ariel Paul; Kathy Perkins; ' + 'Mariah Hermsmeyer (artwork); Diana López Tavares (artwork)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/area-model-introduction', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/area-model-introduction/latest/area-model-introduction-600.png'}, + { 'slug': 'area-model-multiplication', + 'is_phet_studio': False, + 'title': 'Area Model Multiplication', + 'description': 'Build rectangles of various sizes and relate multiplication to area. ' + 'Discover new strategies for multiplying large numbers. Use the game screen ' + 'to test your problem solving strategies!', + 'learning_goals': 'Recognize that area represents the product of two numbers and is ' + 'additive.
Represent a multiplication problem as the area of a ' + 'rectangle, proportionally or using generic area.
Develop and ' + 'justify a strategy to determine the product of two multi-digit numbers ' + 'by representing the product as an area or the sum of areas.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Multiplication', 'Factors', 'Area Model'], + 'grades': ['elementary', 'middle'], + 'version': '1.3.9', + 'released': '2018-04-25', + 'updated': '2026-03-27', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lv', + 'mg', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'qu', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'tg', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['area-model-introduction', 'area-model-decimals', 'area-model-algebra'], + 'design_team': 'Amy Hanson (lead designer) ; Amanda McGarry (co-lead) ; Jonathan Olson ' + '(developer) ; Karina Hensberry; Susan Miller; Ariel Paul; Kathy Perkins; ' + 'Mariah Hermsmeyer (artwork); Diana López Tavares (artwork)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/area-model-multiplication', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/area-model-multiplication/latest/area-model-multiplication-600.png'}, + { 'slug': 'arithmetic', + 'is_phet_studio': False, + 'title': 'Arithmetic', + 'description': 'Remember your multiplication tables? Practice your multiplication, ' + 'division, and factoring skills with this exciting game. No calculators ' + 'allowed!', + 'learning_goals': 'Explain how multiplication tables help understand multiplication, ' + 'factoring, and division.
Use an array model to understand ' + 'multiplication, factoring, and division.
Increase accuracy in ' + 'multiplying, factoring and dividing.
Develop multiple strategies ' + 'for arithmetic problems.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Multiplication', 'Division', 'Factoring'], + 'grades': ['elementary', 'middle'], + 'version': '1.2.8', + 'released': '2015-12-17', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'az', + 'be', + 'bg', + 'bi', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fi', + 'fo', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mg', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'pr', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sn', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['area-builder', 'fraction-matcher', 'make-a-ten', 'area-model-introduction'], + 'design_team': 'Amanda McGarry (lead); Michael Dubson (co-lead); John Blanco (developer); ' + 'Bryce Gruneich; Karina K.R. Hensberry; Trish Loeblein; Ariel Paul; Kathy ' + 'Perkins; Beth Stade', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/arithmetic', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/arithmetic/latest/arithmetic-600.png'}, + { 'slug': 'atomic-interactions', + 'is_phet_studio': False, + 'title': 'Atomic Interactions', + 'description': 'Explore the interactions between various combinations of two atoms. ' + 'Observe the total force acting on the atoms or the individual attractive ' + 'and repulsive forces. Customize the attraction to see how changing the ' + 'atomic diameter and interaction strength affects the interaction.', + 'learning_goals': 'Explain how attractive and repulsive forces govern the interaction ' + 'between atoms.
Describe the effect of potential well depth on ' + 'atomic interactions.
Contrast the potential well of a bonded pair ' + 'of atoms with unbonded pairs.
Describe bonds as dynamic and bond ' + 'lengths as average distances.', + 'subjects': ['physics', 'chemistry'], + 'topics': ['heat-and-thermo', 'general-chemistry'], + 'detail_topics': ['Atomic Interactions', 'Interaction Potential', 'Van der Waals Force'], + 'grades': ['high', 'university'], + 'version': '1.2.21', + 'released': '2016-09-26', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sn', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['states-of-matter', 'states-of-matter-basics'], + 'design_team': 'Yuen-ying Carpenter (lead designer, HTML5); Amy Rouinfar (lead designer, ' + 'HTML5); Paul Beale (lead designer, Java); Sarah McKagan (lead designer, ' + 'Java); Emily Moore (lead designer, Java); Noah Podelefsky (lead designer, ' + 'Java); John Blanco (developer); Wendy Adams; Jack Barbera ; Amy Hanson; ' + 'Kelly Lancaster; Ariel Paul; Kathy Perkins; Carl Wieman', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/atomic-interactions', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/atomic-interactions/latest/atomic-interactions-600.png'}, + { 'slug': 'balancing-act', + 'is_phet_studio': False, + 'title': 'Balancing Act', + 'description': 'Play with objects on a teeter totter to learn about balance. Test what ' + "you've learned by trying the Balance Challenge game.", + 'learning_goals': 'Predict how objects of various masses can be used to make a plank ' + 'balance.
Predict how changing the positions of the masses on the ' + 'plank will affect the motion of the plank.
Write rules to predict ' + 'which way a plank will tilt when objects are placed on it.
Use your ' + 'rules to solve puzzles about balancing.', + 'subjects': ['physics', 'math'], + 'topics': ['motion', 'math-applications'], + 'detail_topics': ['Torque', 'Rotational Equilibrium', 'Lever Arm'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.3.7', + 'released': '2014-05-15', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'ku_TR', + 'lg', + 'lt', + 'lv', + 'mi', + 'mk', + 'ml', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sn', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['forces-and-motion-basics'], + 'design_team': 'Kathy Perkins (lead); Ariel Paul (lead); John Blanco (lead, developer); ' + 'Michael Dubson; Patricia Loeblein', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/balancing-act', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/balancing-act/latest/balancing-act-600.png'}, + { 'slug': 'balancing-chemical-equations', + 'is_phet_studio': True, + 'title': 'Balancing Chemical Equations', + 'description': 'How do you know if a chemical equation is balanced? What can you change to ' + 'balance an equation? Play a game to test your ideas!', + 'learning_goals': 'Balance a chemical equation.
Recognize that the number of atoms of ' + 'each element is conserved in a chemical reaction.
Describe the ' + 'difference between coefficients and subscripts in a chemical ' + 'equation.
Translate from symbolic to molecular representations of ' + 'matter.', + 'subjects': ['chemistry'], + 'topics': ['general-chemistry'], + 'detail_topics': ['Chemical Equations', 'Conservation of Mass'], + 'grades': ['middle', 'high', 'university'], + 'version': '2.1.2', + 'released': '2014-07-31', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mg', + 'mi', + 'mk', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'or', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'se', + 'si', + 'sk', + 'sl', + 'sn', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['reactants-products-and-leftovers', 'build-a-molecule'], + 'design_team': 'Yuen-ying Carpenter (lead); Chris Malley (developer); Kelly Lancaster; ' + 'Julia Chamberlain; Patricia Loeblein; Emily B. Moore; Robert Parson; Ariel ' + 'Paul; Kathy Perkins; Amy Rouinfar; Nancy Salpepi', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/balancing-chemical-equations', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/balancing-chemical-equations/latest/balancing-chemical-equations-600.png'}, + { 'slug': 'balloons-and-static-electricity', + 'is_phet_studio': False, + 'title': 'Balloons and Static Electricity', + 'description': 'Grab a balloon to explore concepts of static electricity such as charge ' + 'transfer, attraction, repulsion, and induced charge.', + 'learning_goals': 'Describe and draw models for common static electricity concepts ' + '(transfer of charge, induction, attraction, repulsion, and ' + 'grounding)
Make predictions about force at a distance for various ' + 'configurations of charge', + 'subjects': ['physics', 'chemistry', 'earth-science'], + 'topics': ['electricity-magnets-and-circuits', 'general-chemistry'], + 'detail_topics': ['Static Electricity', 'Electric Charges', 'Electric Force'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.5.26', + 'released': '2013-08-16', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mk', + 'ml', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'se', + 'si', + 'sk', + 'sl', + 'sn', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['john-travoltage'], + 'design_team': 'Noah Podolefsky (lead); Sam Reid (lead, developer); Jesse Greenberg; Trish ' + 'Loeblein; Emily B. Moore; Ariel Paul; Kathy Perkins; Taliesin Smith; ' + 'Sharon Siman-Tov (graphic arts); Wendy Adams (interviewer)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/balloons-and-static-electricity', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/balloons-and-static-electricity/latest/balloons-and-static-electricity-600.png'}, + { 'slug': 'beers-law-lab', + 'is_phet_studio': True, + 'title': "Beer's Law Lab", + 'description': '“The thicker the glass, the darker the brew, the less the light that ' + 'passes through.” Make colorful concentrated and dilute solutions and ' + 'explore how much light they absorb and transmit using a virtual ' + 'spectrophotometer!', + 'learning_goals': 'Describe the relationships between volume and amount of solute to ' + 'solution concentration
Explain qualitatively the relationship ' + 'between solution color and concentration
Predict and explain how ' + 'solution concentration will change for adding or removing: water, ' + 'solute, and/or solution
Calculate the concentration of solutions in ' + 'units of molarity (mol/L)
Design a procedure for creating a ' + 'solution of a given concentration
Identify when a solution is ' + 'saturated and predict how concentration will change for adding or ' + 'removing: water, solute, and/or solution
Describe the relationship ' + 'between the solution concentration and the intensity of light that is ' + 'absorbed/transmitted
Describe the relationship between ' + 'absorbance, molar absorptivity, path length, and concentration in ' + 'Beer’s Law
Predict how the intensity of light ' + 'absorbed/transmitted will change with changes in solution type, ' + 'solution concentration, container width, or light source, and explain ' + 'why', + 'subjects': ['chemistry'], + 'topics': ['general-chemistry'], + 'detail_topics': ['Light', 'Concentration', 'Molarity'], + 'grades': ['high', 'university'], + 'version': '1.8.7', + 'released': '2013-08-16', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mi', + 'mk', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'or', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sh', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'ti', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['concentration', 'molarity', 'molecules-and-light', 'bending-light'], + 'design_team': 'Julia Chamberlain (lead); Chris Malley (developer); Kelly Lancaster; Emily ' + 'B. Moore; Ariel Paul; Kathy Perkins; Amy Rouinfar; Diana López Tavares; ' + 'Nancy Salpepi', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/beers-law-lab', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/beers-law-lab/latest/beers-law-lab-600.png'}, + { 'slug': 'bending-light', + 'is_phet_studio': False, + 'title': 'Bending Light', + 'description': 'Explore bending of light between two media with different indices of ' + 'refraction. See how changing from air to water to glass changes the ' + 'bending angle. Play with prisms of different shapes and make rainbows.', + 'learning_goals': 'Explain how light bends at the interface between two media and what ' + 'determines the angle.
Apply Snell’s law to a laser beam incident on ' + 'the interface between media.
Describe how the speed and wavelength ' + 'of light changes in different media.
Describe the effect of ' + 'changing wavelength on the angle of refraction.
Explain how a prism ' + 'creates a rainbow.', + 'subjects': ['physics'], + 'topics': ['light-and-radiation'], + 'detail_topics': ['Optics', 'Light', 'Refraction'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.2.5', + 'released': '2015-12-23', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'ee', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mi', + 'mk', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'or', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sd', + 'se', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'beers-law-lab', + 'color-vision', + 'wave-interference', + 'geometric-optics', + 'geometric-optics-basics'], + 'design_team': 'Amy Rouinfar (lead); Sam Reid (developer); Noah Podolefsky; Trish ' + 'Loeblein; Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/bending-light', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/bending-light/latest/bending-light-600.png'}, + { 'slug': 'blackbody-spectrum', + 'is_phet_studio': False, + 'title': 'Blackbody Spectrum', + 'description': 'How does the blackbody spectrum of the sun compare to visible light? Learn ' + 'about the blackbody spectrum of Sirius A, the sun, a light bulb, and the ' + 'earth. Adjust the temperature to see the wavelength and intensity of the ' + 'spectrum change. View the color of the peak of the spectral curve.', + 'learning_goals': 'Describe what happens to the blackbody spectrum as you increase or ' + 'decrease the temperature. What happens to the shape of the curve and ' + 'the peak of this curve?
Describe the blackbody spectrum of a light ' + 'bulb. Why do light bulbs get hot? Do they seem efficient?
Imagine ' + 'that you see 2 hot, glowing objects--one is glowing orange and the ' + 'other is glowing blue. Which one is hotter?
Find the relationship ' + 'between the temperature and the wavelength at the peak of the curve.', + 'subjects': ['physics', 'chemistry', 'earth-science'], + 'topics': ['quantum-phenomena', 'light-and-radiation', 'quantum-chemistry'], + 'detail_topics': ['Quantum Mechanics', 'Blackbody', 'Astronomy'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.0.26', + 'released': '2019-07-06', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lv', + 'mg', + 'mn', + 'ms', + 'mt', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['color-vision', 'greenhouse-effect'], + 'design_team': 'Arnab Purkayastha (developer); Saurabh Totey (developer); Martin Veillette ' + '(developer); Diana López Tavares (lead designer); Michael Dubson (original ' + 'designer); Wendy Adams; John Blanco; Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/blackbody-spectrum', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/blackbody-spectrum/latest/blackbody-spectrum-600.png'}, + { 'slug': 'build-a-fraction', + 'is_phet_studio': False, + 'title': 'Build a Fraction', + 'description': 'Build fractions from shapes and numbers to earn stars in this fractions ' + 'game or explore in the Fractions Lab. Challenge yourself on any level you ' + 'like. Try to collect lots of stars!', + 'learning_goals': 'Build equivalent fractions using numbers and pictures
Compare ' + 'fractions using numbers and patterns
Recognize equivalent ' + 'simplified and unsimplified fractions
Note: Build A Fraction ' + 'expands on ideas from the Fraction Intro and Fraction Matcher sims or ' + 'can be used as a stand-alone tool', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Fractions', 'Equivalent Fractions', 'Mixed Numbers'], + 'grades': ['elementary', 'middle'], + 'version': '1.0.31', + 'released': '2019-01-29', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fi', + 'fo', + 'fr', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mg', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'my', + 'nl', + 'ny', + 'om', + 'pl', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sd', + 'si', + 'sk', + 'sl', + 'sn', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'wo', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['fraction-matcher', 'fractions-intro', 'fractions-mixed-numbers'], + 'design_team': 'Ariel Paul (lead designer); Jonathan Olson (lead developer); Sam Reid ' + '(original developer); Martin Veillette (developer); Michael Dubson; Karina ' + 'K. R. Hensberry; Trish Loeblein; Amanda McGarry; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/build-a-fraction', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/build-a-fraction/latest/build-a-fraction-600.png'}, + { 'slug': 'build-a-molecule', + 'is_phet_studio': False, + 'title': 'Build a Molecule', + 'description': 'Starting from atoms, see how many molecules you can build. Collect your ' + 'molecules and view them in 3D!', + 'learning_goals': 'Describe the difference between an atom and a molecule.
Build ' + 'simple molecules from atoms.
Distinguish between the coefficient ' + 'and subscript in a chemical formula.
Construct molecules from the ' + 'chemical formula.
Associate common molecule names with multiple ' + 'representations.
Experiment with combining atoms to build larger ' + 'molecules.', + 'subjects': ['chemistry'], + 'topics': ['general-chemistry'], + 'detail_topics': ['Molecules', 'Molecular Formula', 'Isomers'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.0.25', + 'released': '2020-10-12', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fi', + 'fo', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mi', + 'mk', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'rw', + 'sk', + 'sl', + 'sn', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'balancing-chemical-equations', + 'build-an-atom', + 'molecule-shapes', + 'molecule-shapes-basics', + 'molecule-polarity'], + 'design_team': 'Denzell Barnett (lead developer); Amy Rouinfar (lead designer); Christine ' + 'Denison; Kelly Lancaster; Emily B. Moore; Jonathan Olson ; Ariel Paul; ' + 'Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/build-a-molecule', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/build-a-molecule/latest/build-a-molecule-600.png'}, + { 'slug': 'build-a-nucleus', + 'is_phet_studio': False, + 'title': 'Build a Nucleus', + 'description': 'How does changing the number of neutrons or protons affect the nuclear ' + 'species, the stability of a nucleus, and its possible decay mechanisms? ' + 'Explore different species, and learn about the common decay paths! ' + 'Visualize these through the chart of the nuclides and discover the inner ' + 'energy levels of the nucleus.', + 'learning_goals': 'Explain how changing the number of neutrons or protons affects the ' + 'atomic number and isotope species.
Describe how different decays ' + 'will change the nucleons in the nucleus and if that changes the symbol ' + 'of the atom being shown and affects parameters like atomic ' + 'number/atomic mass.
Predict the shell model depiction of an isotope ' + 'based on the isotope symbol or name.
Find the isotope on the ' + 'nuclide chart using the symbol, name, or the number of protons and ' + 'neutrons.
Predict the resulting isotope and the decay path of an ' + 'isotope from its position on the nuclide chart.', + 'subjects': ['physics', 'chemistry'], + 'topics': [], + 'detail_topics': ['Nuclear Decay', 'Nuclear Physics', 'Isotope'], + 'grades': ['high', 'university'], + 'version': '1.1.10', + 'released': '2023-10-18', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sn', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_TW', + 'zu'], + 'related': ['isotopes-and-atomic-mass'], + 'design_team': 'Luisa Vargas (lead designer); Ariel Paul (lead designer); Luisa Vargas ' + '(developer); Michael Kauzmann (developer); Chris Klusendorf (developer); ' + 'Marla Schulz (developer); Martin Veillette (developer); Jason Donev ' + '(University of Calgary); Amy Rouinfar; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/build-a-nucleus', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/build-a-nucleus/latest/build-a-nucleus-600.png'}, + { 'slug': 'build-an-atom', + 'is_phet_studio': True, + 'title': 'Build an Atom', + 'description': 'Build an atom out of protons, neutrons, and electrons, and see how the ' + 'element, charge, and mass change. Then play a game to test your ideas!', + 'learning_goals': 'Use the number of protons, neutrons, and electrons to draw a model of ' + 'the atom, identify the element, and determine the mass and ' + 'charge.
Predict how addition or subtraction of a proton, neutron, ' + 'or electron will change the element, the charge, and the mass.
Use ' + 'the element name, mass, and charge to determine the number of protons, ' + 'neutrons, and electrons.
Define proton, neutron, electron, atom, ' + 'and ion.
Generate an isotopic symbol for an atom, given the number ' + 'of protons, neutrons, and electrons.', + 'subjects': ['physics', 'chemistry'], + 'topics': ['general-chemistry'], + 'detail_topics': ['Atomic Structure', 'Atoms', 'Atomic Nuclei'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.9.3', + 'released': '2013-08-08', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'ee', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mi', + 'mk', + 'mn', + 'mo', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'or', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sd', + 'se', + 'si', + 'sk', + 'sl', + 'sn', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'ti', + 'tk', + 'tn', + 'tr', + 'tw', + 'ua', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'molecule-shapes', + 'molecule-shapes-basics', + 'isotopes-and-atomic-mass', + 'states-of-matter', + 'states-of-matter-basics', + 'build-a-molecule'], + 'design_team': 'Kelly Lancaster (lead); John Blanco (developer); Sam Reid (developer); ' + 'Agustín Vallejo (developer); Jack Barbera; Suzanne Brahmia; Julia ' + 'Chamberlain; Yuen-ying Carpenter; Patricia Loeblein; Emily B. Moore; ' + 'Robert Parson; Ariel Paul; Kathy Perkins; Amy Rouinfar; Nancy Salpepi; ' + 'Sharon Siman-Tov; Taliesin Smith', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/build-an-atom', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/build-an-atom/latest/build-an-atom-600.png'}, + { 'slug': 'buoyancy', + 'is_phet_studio': True, + 'title': 'Buoyancy', + 'description': 'When will objects float, and when will they sink? Learn how buoyancy works ' + 'and the forces involved by experimenting with objects, like blocks, ' + 'materials with different shapes, a bottle with substances inside, and ' + 'blocks in a boat, in a pool with a fluid that can be modified.', + 'learning_goals': 'Identify the variables that affect whether an object sinks or floats in ' + 'a liquid.
Predict whether an object will sink or float when placed ' + 'in a liquid.
Describe the forces that act on a completely or ' + 'partially submerged object.
Predict the apparent weight of a ' + 'completely or partially submerged object of known mass and ' + 'volume.
Relate the buoyant force on an object to the weight of the ' + 'liquid it displaces (Archimedes’ Principle).
Describe how the ' + "buoyant force is related to an object's relative density to the " + 'fluid.
Compare the buoyancy of objects with different shapes and ' + 'the same volume.
Explain how to control that an object floats, ' + 'sinks, or stays at depth without sinking to the bottom.
Understand ' + 'the basic principle of a submarine.
Calculate the weight that a ' + 'boat can carry without sinking.', + 'subjects': ['physics'], + 'topics': ['motion'], + 'detail_topics': ['Buoyancy', 'Density', 'Balanced Force'], + 'grades': ['high', 'university'], + 'version': '1.2.12', + 'released': '2024-10-09', + 'updated': '2026-06-24', + 'is_new': True, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_LY', + 'be', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'en_GB', + 'es', + 'es_PE', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'is', + 'it', + 'iw', + 'ja', + 'km', + 'kn', + 'ko', + 'ku', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'sk', + 'sl', + 'sr', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tr', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_TW'], + 'related': ['under-pressure', 'density', 'buoyancy-basics'], + 'design_team': 'Diana López (lead designer); Agustín Vallejo (developer); Michael Kauzmann ' + '(developer); Sam Reid (developer); Jonathan Olson (developer); Amy ' + 'Roinfar; Ariel Paul; Kathy Perkins ; Wendy Adams; Kelly Lancaster ; Trish ' + 'Loeblein; Chris Malley; Archie Paulson; Carl Wieman', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/buoyancy', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/buoyancy/latest/buoyancy-600.png'}, + { 'slug': 'buoyancy-basics', + 'is_phet_studio': True, + 'title': 'Buoyancy: Basics', + 'description': 'When will objects float, and when will they sink? Learn how buoyancy works ' + 'by experimenting with different blocks, modifying their volume and mass, ' + 'and placing them in the pool with fluids of different densities. Analyze ' + 'the forces generated when the block is inside the pool, and identify the ' + 'variables that affect the buoyant force.', + 'learning_goals': 'Identify the variables that affect whether an object sinks or floats in ' + 'a liquid.
Predict whether an object will sink or float when placed ' + 'in a liquid.
Describe the forces that act on a completely or ' + 'partially submerged object.
Describe what apparent weight is.', + 'subjects': ['physics'], + 'topics': ['motion'], + 'detail_topics': ['Buoyancy', 'Density', 'Fluids'], + 'grades': ['middle', 'high'], + 'version': '1.2.11', + 'released': '2024-10-10', + 'updated': '2026-06-24', + 'is_new': True, + 'locales': [ 'ar', + 'ar_LY', + 'be', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'en_GB', + 'es', + 'es_PE', + 'fa', + 'fa_DA', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'is', + 'it', + 'iw', + 'kn', + 'ko', + 'ku', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'sk', + 'sl', + 'sr', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tr', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_TW'], + 'related': ['under-pressure', 'density', 'buoyancy'], + 'design_team': 'Diana López (lead designer); Agustín Vallejo (developer); Michael Kauzmann ' + '(developer); Sam Reid (developer); Jonathan Olson (developer); Amy ' + 'Roinfar; Ariel Paul; Kathy Perkins ; Wendy Adams; Kelly Lancaster ; Trish ' + 'Loeblein; Chris Malley; Archie Paulson; Carl Wieman', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/buoyancy-basics', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/buoyancy-basics/latest/buoyancy-basics-600.png'}, + { 'slug': 'calculus-grapher', + 'is_phet_studio': True, + 'title': 'Calculus Grapher', + 'description': 'Manipulate a graph of any function and see graphs of its integral, first ' + 'derivative, and second derivative. Drag the tangent line along the curve, ' + 'and accumulate area under the curve.', + 'learning_goals': 'Given a function, predict the integral, first derivative, or second ' + 'derivative curves
Identify critical points and compare that x-value ' + 'across multiple graphs using the reference line
Identify where ' + 'slope is changing, constant, or zero, and relate the slope of the ' + 'tangent line to the derivative curve
Identify where area under the ' + 'curve is positive or negative, and relate the net signed area to the ' + 'integral curve
Explain the effect of a discontinuity in a function ' + 'on the derivative and the integral curves', + 'subjects': ['physics', 'math'], + 'topics': ['motion', 'math-concepts'], + 'detail_topics': ['Derivative', 'Integral'], + 'grades': ['high', 'university'], + 'version': '1.1.1', + 'released': '2023-04-17', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'is', + 'it', + 'iw', + 'ki', + 'ko', + 'ku', + 'mi', + 'mn', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'sl', + 'sp', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'tr', + 'uz', + 'vi', + 'yo', + 'zh_TW', + 'zu'], + 'related': [], + 'design_team': 'Amanda McGarry (lead designer); Catherine Carter (lead designer); Michael ' + 'Dubson (original designer and developer); Martin Veillette (lead ' + 'developer); Chris Malley (developer); Brandon Li (developer); Ariel Paul; ' + 'Kathy Perkins; Taliesin Smith', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/calculus-grapher', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/calculus-grapher/latest/calculus-grapher-600.png'}, + { 'slug': 'capacitor-lab-basics', + 'is_phet_studio': False, + 'title': 'Capacitor Lab: Basics', + 'description': 'Explore how a capacitor works! Change the size of the plates and the ' + 'distance between them. Change the voltage and see charges build up on the ' + 'plates. View the electric field, and measure the voltage. Connect a ' + 'charged capacitor to a light bulb and observe a discharging RC circuit.', + 'learning_goals': 'Explain the relationships between voltage, charge, stored energy, and ' + 'capacitance
Predict how capacitance changes when the plate area or ' + 'plate separation changes
Describe how charge drains away from a ' + 'capacitor into a light bulb', + 'subjects': ['physics'], + 'topics': ['electricity-magnets-and-circuits'], + 'detail_topics': ['Circuits', 'RC Circuit', 'Capacitance'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.6.39', + 'released': '2017-11-15', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['circuit-construction-kit-dc', 'circuit-construction-kit-ac'], + 'design_team': 'Amy Rouinfar (lead designer); Andrew Adare (developer); Jesse Greenberg ' + '(developer); Chris Malley (developer); Jonathan Olson (developer); Emily ' + 'Randall (developer); Emily Moore; Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/capacitor-lab-basics', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/capacitor-lab-basics/latest/capacitor-lab-basics-600.png'}, + { 'slug': 'center-and-variability', + 'is_phet_studio': True, + 'title': 'Center and Variability', + 'description': 'Center and Variability allows students to explore and compare measures of ' + 'center and variability, including mean, median, range, IQR, and MAD.', + 'learning_goals': 'Describe how the median is derived.
Predict the effects of an ' + 'outlier on both the mean and median.
Compare the mean and median. ' + '
Make sense of and determine the reasonableness of the mean and ' + 'median of a data set by looking at the values and/or the distribution. ' + '
Predict the mean and median of a data set.
Compare measures of ' + 'variability such as IQR and MAD.
Explore measures of center and ' + 'variability from different distributions.
Describe what the range, ' + 'IQR, and MAD measure about a distribution.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Central Tendency', 'Mean', 'Median'], + 'grades': ['middle'], + 'version': '1.2.6', + 'released': '2023-11-01', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SY', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fr', + 'gu', + 'ha', + 'hi', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kn', + 'ko', + 'ku', + 'lg', + 'mn', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'rw', + 'sk', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uz', + 'vi', + 'yo', + 'zh_TW', + 'zu'], + 'related': ['mean-share-and-balance'], + 'design_team': 'Amanda McGarry (lead designer); Catherine Carter (co-lead designer); Kelly ' + 'Findley ; Marilyn Hartzell ; Ariel Paul; Kathy Perkins; Taliesin Smith; ' + 'David Webb ; Chris Klusendorf (developer); Sam Reid (developer); Marla ' + 'Shultz (developer); Matt Blackman (developer)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/center-and-variability', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/center-and-variability/latest/center-and-variability-600.png'}, + { 'slug': 'charges-and-fields', + 'is_phet_studio': False, + 'title': 'Charges and Fields', + 'description': 'Arrange positive and negative charges in space and view the resulting ' + 'electric field and electrostatic potential. Plot equipotential lines and ' + 'discover their relationship to the electric field. Create models of ' + 'dipoles, capacitors, and more!', + 'learning_goals': 'Determine the variables that affect the strength and direction of the ' + 'electric field for a static arrangement of charges.
Investigate the ' + 'variables that affect the strength of the electrostatic potential ' + '(voltage).
Explain equipotential lines and compare them to the ' + 'electric field lines.
For an arrangement of static charges, predict ' + 'the electric field lines. Verify the prediction using vector addition.', + 'subjects': ['physics'], + 'topics': ['electricity-magnets-and-circuits'], + 'detail_topics': ['Voltage', 'Electric Charges', 'Electrostatics'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.0.67', + 'released': '2016-09-07', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sn', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['coulombs-law'], + 'design_team': 'Amy Rouinfar (lead designer - HTML5); Michael Dubson (lead ' + 'designer/developer - Flash); Andrew Adare (developer); Martin Veillette ' + '(developer); Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/charges-and-fields', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/charges-and-fields/latest/charges-and-fields-600.png'}, + { 'slug': 'circuit-construction-kit-ac', + 'is_phet_studio': False, + 'title': 'Circuit Construction Kit: AC', + 'description': 'Experiment with an electronics kit! Build circuits with batteries, ' + 'resistors, ideal and non-Ohmic light bulbs, fuses, and switches. Build ' + 'circuits with AC voltage sources, batteries, resistors, capacitors, ' + 'inductors, fuses, and switches. Take measurements with a lifelike ammeter ' + 'and voltmeter and graph the current and voltage as a function of time. ' + 'View the circuit as a schematic diagram or switch to a lifelike view.', + 'learning_goals': 'Explain basic electricity relationships in series and parallel ' + 'circuits.
Use an ammeter and voltmeter to take readings in ' + 'circuits.
Provide reasoning to explain the measurements and ' + 'relationships in circuits.
Build circuits from schematic ' + 'drawings.
Determine if common objects are conductors or ' + 'insulators.
Compare and contrast AC and DC circuits.
Describe ' + 'how capacitors and inductors behave in a circuit.
Experimentally ' + 'determine the RC time constant.
Construct RLC circuits and ' + 'determine the conditions for resonance.', + 'subjects': ['physics'], + 'topics': ['electricity-magnets-and-circuits'], + 'detail_topics': ["Ohm's Law", "Kirchoff's Law", 'AC Circuits'], + 'grades': ['high', 'university'], + 'version': '1.0.23', + 'released': '2021-12-15', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'ohms-law', + 'resistance-in-a-wire', + 'circuit-construction-kit-dc', + 'capacitor-lab-basics', + 'circuit-construction-kit-ac-virtual-lab'], + 'design_team': 'Amy Rouinfar (lead designer); Sam Reid (lead developer); Denzell Barnett ' + '(developer); Wendy Adams; Michael Dubson; Noah Finkelstein; Chris Keller; ' + 'Ariel Paul; Kathy Perkins; Carl Wieman', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/circuit-construction-kit-ac', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/circuit-construction-kit-ac/latest/circuit-construction-kit-ac-600.png'}, + { 'slug': 'circuit-construction-kit-ac-virtual-lab', + 'is_phet_studio': False, + 'title': 'Circuit Construction Kit: AC - Virtual Lab', + 'description': 'Do you like Circuit Construction Kit: AC, but want to use only in-line ' + 'ammeters? This is the sim for you! Experiment with an electronics kit. ' + 'Build circuits with AC voltage sources, batteries, resistors, capacitors, ' + 'inductors, fuses, and switches. Take measurements with a lifelike ammeter ' + 'and voltmeter and graph the current and voltage as a function of time. ' + 'View the circuit as a schematic diagram or switch to a lifelike view.', + 'learning_goals': 'Explain basic electricity relationships in series and parallel ' + 'circuits.
Use an ammeter and voltmeter to take readings in ' + 'circuits.
Provide reasoning to explain the measurements and ' + 'relationships in circuits.
Build circuits from schematic ' + 'drawings.
Determine if common objects are conductors or ' + 'insulators.
Compare and contrast AC and DC circuits.
Describe ' + 'how capacitors and inductors behave in a circuit.
Experimentally ' + 'determine the RC time constant.
Construct RLC circuits and ' + 'determine the conditions for resonance.', + 'subjects': ['physics'], + 'topics': ['electricity-magnets-and-circuits'], + 'detail_topics': ["Virtual Lab - Ohm's Law", "Kirchoff's Law", 'AC Circuits'], + 'grades': ['high', 'university'], + 'version': '1.0.22', + 'released': '2021-12-15', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['circuit-construction-kit-ac'], + 'design_team': 'Amy Rouinfar (lead designer); Sam Reid (lead developer); Denzell Barnett ' + '(developer); Wendy Adams; Michael Dubson; Noah Finkelstein; Chris Keller; ' + 'Ariel Paul; Kathy Perkins; Carl Wieman', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/circuit-construction-kit-ac-virtual-lab', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/circuit-construction-kit-ac-virtual-lab/latest/circuit-construction-kit-ac-virtual-lab-600.png'}, + { 'slug': 'circuit-construction-kit-dc', + 'is_phet_studio': True, + 'title': 'Circuit Construction Kit: DC', + 'description': 'Experiment with an electronics kit! Build circuits with batteries, ' + 'resistors, ideal and non-Ohmic light bulbs, fuses, and switches. Determine ' + 'if everyday objects are conductors or insulators, and take measurements ' + 'with an ammeter and voltmeter. View the circuit as a schematic diagram, or ' + 'switch to a lifelike view.', + 'learning_goals': 'Explore basic electricity relationships.
Explain basic electricity ' + 'relationships in series and parallel circuits.
Use an ammeter and ' + 'voltmeter to take readings in circuits.
Provide reasoning to ' + 'explain the measurements and relationships in circuits.
Build ' + 'circuits from schematic drawings.
Determine if common objects are ' + 'conductors or insulators.', + 'subjects': ['physics'], + 'topics': ['electricity-magnets-and-circuits'], + 'detail_topics': ["Ohm's Law", "Kirchoff's Law", 'Series Circuit'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.5.2', + 'released': '2017-10-30', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'ak', + 'am', + 'ar', + 'ar_MA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'ku_TR', + 'lg', + 'lt', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'ohms-law', + 'resistance-in-a-wire', + 'circuit-construction-kit-dc-virtual-lab', + 'capacitor-lab-basics', + 'circuit-construction-kit-ac'], + 'design_team': 'Amy Rouinfar (lead designer); Sam Reid (lead developer); Denzell Barnett ' + '(developer); Matthew Blackman (developer); Wendy Adams; Michael Dubson; ' + 'Noah Finkelstein; Chris Keller; Ariel Paul; Kathy Perkins; Taliesin Smith; ' + 'Carl Wieman; Bryce Gruneich (graphic arts); Mariah Hermsmeyer (graphic ' + 'arts); Cheryl McCutchan (graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/circuit-construction-kit-dc', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/circuit-construction-kit-dc/latest/circuit-construction-kit-dc-600.png'}, + { 'slug': 'circuit-construction-kit-dc-virtual-lab', + 'is_phet_studio': True, + 'title': 'Circuit Construction Kit: DC - Virtual Lab', + 'description': 'Do you like Circuit Construction Kit: DC, but want to use only in-line ' + 'ammeters? This is the sim for you! Experiment with an electronics kit. ' + 'Build circuits with batteries, resistors, ideal and non-Ohmic light bulbs, ' + 'fuses, and switches. Determine if everyday objects are conductors or ' + 'insulators, and take measurements with a lifelike ammeter and voltmeter. ' + 'View the circuit as a schematic diagram, or switch to a lifelike view.', + 'learning_goals': 'Explore basic electricity relationships.
Explain basic electricity ' + 'relationships in series and parallel circuits.
Use an ammeter and ' + 'voltmeter to take readings in circuits.
Provide reasoning to ' + 'explain the measurements and relationships in circuits.
Build ' + 'circuits from schematic drawings.
Determine if common objects are ' + 'conductors or insulators.', + 'subjects': ['physics'], + 'topics': ['electricity-magnets-and-circuits'], + 'detail_topics': ["Virtual Lab - Ohm's Law", "Kirchoff's Law", 'Series Circuit'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.5.2', + 'released': '2017-10-30', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'ak', + 'am', + 'ar', + 'ar_MA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'ku_TR', + 'lg', + 'lt', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['ohms-law', 'resistance-in-a-wire', 'circuit-construction-kit-dc'], + 'design_team': 'Amy Rouinfar (lead designer); Sam Reid (lead developer); Denzell Barnett ' + '(developer); Matthew Blackman (developer); Wendy Adams; Michael Dubson; ' + 'Noah Finkelstein; Chris Keller; Ariel Paul; Kathy Perkins; Taliesin Smith; ' + 'Carl Wieman; Bryce Gruneich (graphic arts); Mariah Hermsmeyer (graphic ' + 'arts); Cheryl McCutchan (graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/circuit-construction-kit-dc-virtual-lab', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/circuit-construction-kit-dc-virtual-lab/latest/circuit-construction-kit-dc-virtual-lab-600.png'}, + { 'slug': 'collision-lab', + 'is_phet_studio': False, + 'title': 'Collision Lab', + 'description': 'Investigate simple collisions in 1D and more complex collisions in 2D. ' + 'Experiment with the number of balls, masses, and initial conditions. Vary ' + 'the elasticity and see how the total momentum and kinetic energy change ' + 'during collisions.', + 'learning_goals': 'Draw "before-and-after" pictures of collisions.
Construct momentum ' + 'vector representations of "before-and-after" collisions.
Apply the ' + 'law of conservation of momentum to solve collision ' + 'problems.
Explain why energy is not conserved and varies in some ' + 'collisions.
Determine the change in mechanical energy in ' + 'collisions of varying elasticity.
Define elasticity.', + 'subjects': ['physics'], + 'topics': ['motion'], + 'detail_topics': ['Conservation of Energy', 'Collisions', 'Elasticity'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.1.23', + 'released': '2021-03-12', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'kk', + 'ko', + 'ku', + 'lg', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['forces-and-motion-basics', 'vector-addition', 'energy-skate-park'], + 'design_team': 'Brandon Li (developer); Jonathon Olson (developer); Martin Veillette ' + '(developer); Amy Rouinfar (lead designer); Michael Dubson ; Trish ' + 'Loeblein; Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/collision-lab', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/collision-lab/latest/collision-lab-600.png'}, + { 'slug': 'color-vision', + 'is_phet_studio': False, + 'title': 'Color Vision', + 'description': 'Make a whole rainbow by mixing red, green, and blue light. Change the ' + 'wavelength of a monochromatic beam or filter white light. View the light ' + 'as a solid beam, or see the individual photons.', + 'learning_goals': 'Determine what color the person sees for various combinations of red, ' + 'green, and blue light.
Describe the color of light that is able to ' + 'pass through different colored filters.', + 'subjects': ['physics', 'biology'], + 'topics': ['light-and-radiation'], + 'detail_topics': ['Photons', 'White Light', 'Monochromatic Light'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.3.7', + 'released': '2014-09-17', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mg', + 'mi', + 'mk', + 'mn', + 'mr', + 'ms', + 'mt', + 'my', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'se', + 'si', + 'sk', + 'sl', + 'sn', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['bending-light', 'blackbody-spectrum'], + 'design_team': 'Bryce Gruneich (lead); Kathy Perkins (lead); Aaron Davis (developer); Ron ' + 'LeMaster (developer); Chris Malley (developer); Sam Reid (developer); ' + 'Wendy Adams; Danielle Harlow; Ariel Paul; Carl Wieman; Mike Fowler ' + '(graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/color-vision', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/color-vision/latest/color-vision-600.png'}, + { 'slug': 'concentration', + 'is_phet_studio': True, + 'title': 'Concentration', + 'description': 'Watch your solution change color as you mix chemicals with water. Then ' + 'check molarity with the concentration meter. What are all the ways you can ' + 'change the concentration of your solution? Switch solutes to compare ' + 'different chemicals and find out how concentrated you can go before you ' + 'hit saturation!', + 'learning_goals': 'Describe the relationships between volume and amount of solute to ' + 'solution concentration.
Explain how solution color and ' + 'concentration are related.
Predict how solution concentration will ' + 'change for any action (or combination of actions) that adds or removes ' + 'water, solute, or solution, and explain why.
Design a procedure for ' + 'creating a solution of a given concentration.
Design and justify a ' + 'procedure for changing a solution from one concentration to ' + 'another.
Identify when a solution is saturated and predict how ' + 'concentration will change for any action or combination of actions ' + 'where water or solute change.', + 'subjects': ['chemistry'], + 'topics': ['general-chemistry'], + 'detail_topics': ['Volume', 'Concentration', 'Moles'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.8.6', + 'released': '2013-10-31', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'or', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'ti', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['beers-law-lab', 'molarity'], + 'design_team': 'Julia Chamberlain (lead); Chris Malley (developer); Kelly Lancaster; Emily ' + 'B. Moore; Ariel Paul; Kathy Perkins; Amy Rouinfar; Diana López Tavares; ' + 'Nancy Salpepi', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/concentration', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/concentration/latest/concentration-600.png'}, + { 'slug': 'coulombs-law', + 'is_phet_studio': False, + 'title': "Coulomb's Law", + 'description': 'Visualize the electrostatic force that two charges exert on each other. ' + 'Observe how changing the sign and magnitude of the charges and the ' + 'distance between them affects the electrostatic force.', + 'learning_goals': 'Relate the electrostatic force magnitude to the charges and the ' + "distance between them
Explain Newton's third law for electrostatic " + 'forces
Use measurements to determine Coulomb’s ' + 'constant
Determine what makes a force attractive or repulsive', + 'subjects': ['physics', 'chemistry'], + 'topics': ['electricity-magnets-and-circuits', 'general-chemistry'], + 'detail_topics': ['Electric Force', "Coulomb's Law", 'Electrostatics'], + 'grades': ['high', 'university'], + 'version': '1.0.32', + 'released': '2018-12-27', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'ko', + 'ku', + 'lg', + 'lv', + 'mn', + 'mr', + 'ms', + 'mt', + 'my', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['gravity-force-lab', 'charges-and-fields', 'gravity-force-lab-basics'], + 'design_team': 'Michael Barlow (lead developer); Jesse Greenberg (developer); Amy Rouinfar ' + '(lead designer); Amy Hanson; Ariel Paul; Kathy Perkins; Mariah Hermsmeyer ' + '(graphic arts); Cheryl McCutchan (graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/coulombs-law', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/coulombs-law/latest/coulombs-law-600.png'}, + { 'slug': 'curve-fitting', + 'is_phet_studio': False, + 'title': 'Curve Fitting', + 'description': 'Drag data points and their error bars and watch the best-fit polynomial ' + 'curve update instantly. You choose the type of fit: linear, quadratic, or ' + 'cubic. The reduced chi-square statistic shows you when the fit is good. Or ' + 'you can try to find the best fit by manually adjusting fit parameters.', + 'learning_goals': 'Explain how the range and uncertainty and number of data points affect ' + 'correlation coefficient and chi squared
Describe how correlation ' + 'coefficient and chi squared can be used to indicate how well a curve ' + 'describes the data relationship
Apply understanding of Curve ' + 'Fitting to designing experiments', + 'subjects': ['physics', 'math'], + 'topics': ['math-applications'], + 'detail_topics': ['Polynomials', 'Error Analysis', 'Data'], + 'grades': ['high', 'university'], + 'version': '1.0.20', + 'released': '2019-11-06', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'kk', + 'ko', + 'ku', + 'lg', + 'lv', + 'mn', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'tk', + 'tr', + 'tw', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['least-squares-regression'], + 'design_team': 'Michael Dubson (lead designer, original developer); Chris Malley ' + '(developer); Jonathan Olson (developer); Saurabh Totey (developer); Martin ' + 'Veillette (developer); Amanda McGarry (lead designer); Trish Loeblein; ' + 'Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/curve-fitting', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/curve-fitting/latest/curve-fitting-600.png'}, + { 'slug': 'density', + 'is_phet_studio': True, + 'title': 'Density', + 'description': 'Why do some materials like wood float in water, and others don’t? Interact ' + 'with blocks of different materials, including a custom option by modifying ' + 'their mass and volume, to explore the effect on the density and discover ' + 'the conditions for sinking or floating in water. Play detective to ' + 'determine the material of each block by comparing its density with the ' + 'values in the table.', + 'learning_goals': "Describe how the concept of density relates to an object's mass and " + 'volume.
Explain how objects of similar mass can have differing ' + 'volume, and how objects of similar volume can have differing ' + "mass.
Explain why changing an object's mass or volume does not " + 'affect its density (ie, understand density as an intensive ' + 'property).
Measure the volume of an object by observing the amount ' + 'of fluid it displaces.
Identify an unknown material by calculating ' + 'its density and comparing to a table of known densities.', + 'subjects': ['physics', 'biology', 'chemistry', 'earth-science'], + 'topics': ['general-chemistry'], + 'detail_topics': ['Volume', 'Mass', 'Density'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.2.10', + 'released': '2022-01-13', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_KW', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sd', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tl', + 'tn', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['under-pressure', 'buoyancy', 'buoyancy-basics'], + 'design_team': 'Diana López Tavares (lead designer); Jonathan Olson (lead developer); ' + 'PixelZoom, Inc. (developer); Sam Reid (developer); Wendy Adams; Kelly ' + 'Lancaster; Trish Loeblein; Ariel Paul; Archie Paulson; Kathy Perkins; Noah ' + 'Podolefsky; Amy Rouninfar; Carl Wieman', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/density', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/density/latest/density-600.png'}, + { 'slug': 'diffusion', + 'is_phet_studio': True, + 'title': 'Diffusion', + 'description': 'Mix two gases to explore diffusion! Experiment with concentration, ' + 'temperature, mass, and radius and determine how these factors affect the ' + 'rate of diffusion.', + 'learning_goals': 'Explain how two gases mix.
Design an experiment to find the factors ' + 'which affect the rate of diffusion.', + 'subjects': ['physics', 'chemistry', 'earth-science'], + 'topics': ['heat-and-thermo', 'general-chemistry'], + 'detail_topics': ['Gas', 'Thermodynamics', 'Diffusion'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.1.7', + 'released': '2019-08-13', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sn', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['gas-properties', 'gases-intro', 'membrane-transport'], + 'design_team': 'Chris Malley (developer); Amy Rouinfar (lead designer); Jack Barbera; John ' + 'Blanco; Michael Dubson; Amy Hanson; Linda Koch; Ron LeMaster; Trish ' + 'Loeblein; Emily B. Moore; Ariel Paul; Dennis Perepelitsa; Kathy Perkins; ' + 'Tom Perkins; Carl Wieman', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/diffusion', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/diffusion/latest/diffusion-600.png'}, + { 'slug': 'energy-forms-and-changes', + 'is_phet_studio': False, + 'title': 'Energy Forms and Changes', + 'description': 'Explore how heating and cooling iron, brick, water, and olive oil adds or ' + 'removes energy. See how energy is transferred between objects. Build your ' + 'own system, with energy sources, changers, and users. Track and visualize ' + 'how energy flows and changes through your system.', + 'learning_goals': 'Predict how energy will flow when objects are heated or cooled, or for ' + 'objects in contact that have different temperatures.
Describe the ' + 'different types of energy and give examples from everyday ' + 'life.
Describe how energy can change from one form of energy into ' + 'another.
Explain conservation of energy in real-life ' + 'systems.
Design a system with energy sources, changers, and users ' + 'and describe how energy flows and changes one form of energy into ' + 'another.
Tell the energy story for real-life systems.', + 'subjects': ['physics', 'chemistry'], + 'topics': ['work-energy-and-power', 'heat-and-thermo', 'general-chemistry'], + 'detail_topics': ['Conservation of Energy', 'Energy Systems', 'Energy Transfer'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.4.24', + 'released': '2019-01-31', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'ko', + 'ku', + 'ku_TR', + 'lg', + 'lo', + 'lt', + 'lv', + 'mi', + 'mk', + 'mn', + 'mr', + 'ms', + 'mt', + 'my', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'energy-skate-park-basics', + 'states-of-matter', + 'states-of-matter-basics', + 'energy-skate-park', + 'greenhouse-effect'], + 'design_team': 'Chris Klusendorf (lead developer); John Blanco (developer); Andrew Adare ' + '(developer); Amy Rouinfar (lead designer); Noah Podolefsky ; Trish ' + 'Loeblein; Emily Moore; Kathy Perkins; Mariah Hermsmeyer (graphic arts); ' + 'Megan Lai (graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/energy-forms-and-changes', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/energy-forms-and-changes/latest/energy-forms-and-changes-600.png'}, + { 'slug': 'energy-skate-park', + 'is_phet_studio': True, + 'title': 'Energy Skate Park', + 'description': 'Learn about the conservation of energy at the skate park! Build tracks, ' + "ramps, and jumps for the skater. View the skater's kinetic energy, " + 'potential energy, and thermal energy as they move along the track. Measure ' + 'the speed and adjust the friction, gravity, and mass.', + 'learning_goals': 'Explain the Conservation of Mechanical Energy using kinetic, ' + 'gravitational potential, and thermal energy.
Describe how changing ' + "the mass, friction, or gravity affects the skater's energy.
Predict " + 'position or estimate speed from energy bar graph or pie ' + 'chart.
Calculate speed or height at one position from information ' + 'about a different position.
Describe what happens to the energy in ' + 'the system when the reference height changes.
Design a skate park ' + 'using the concepts of mechanical energy and energy conservation.', + 'subjects': ['physics'], + 'topics': ['motion', 'work-energy-and-power'], + 'detail_topics': ['Thermal Energy', 'Energy', 'Conservation of Energy'], + 'grades': ['high', 'university'], + 'version': '1.5.1', + 'released': '2020-12-15', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'my', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'energy-skate-park-basics', + 'hookes-law', + 'pendulum-lab', + 'pendulum-lab', + 'masses-and-springs', + 'masses-and-springs'], + 'design_team': 'Ariel Paul (lead designer); Amy Rouinfar (lead designer); Sam Reid (lead ' + 'developer); Jesse Greenberg (lead developer); Marla Schulz (developer); ' + 'Agustín Vallejo (developer); Lusia Vargas (developer); Michael Dubson; ' + 'Bryce Gruneich; Trish Loeblein; Diana López Tavares; Emily B. Moore; Kathy ' + 'Perkins; Noah Podolefsky', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/energy-skate-park', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/energy-skate-park/latest/energy-skate-park-600.png'}, + { 'slug': 'energy-skate-park-basics', + 'is_phet_studio': True, + 'title': 'Energy Skate Park: Basics', + 'description': 'Learn about conservation of energy with a skater gal! Explore different ' + 'tracks and view the kinetic energy, potential energy and friction as she ' + 'moves. Build your own tracks, ramps, and jumps for the skater.', + 'learning_goals': 'Explain the Conservation of Mechanical Energy concept using kinetic ' + 'energy (KE) and gravitational potential energy (PE).
Describe how ' + 'the Energy Bar and Pie Charts relate to position and speed.
Explain ' + 'how changing the Skater Mass affects energy.
Explain how changing ' + 'the Track Friction affects energy.
Predict position or estimate ' + 'speed from Energy Bar and Pie Charts.
Calculate speed or height at ' + 'one position from information about a different position.
Calculate ' + 'KE and PE at one position from information about a different ' + 'position.
Design a skate park using the concepts of mechanical ' + 'energy and energy conservation.', + 'subjects': ['physics'], + 'topics': ['motion', 'work-energy-and-power'], + 'detail_topics': ['Thermal Energy', 'Energy', 'Conservation of Energy'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.5.1', + 'released': '2014-11-10', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_CO', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mi', + 'mk', + 'mn', + 'mr', + 'ms', + 'mt', + 'my', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'hookes-law', + 'masses-and-springs', + 'energy-forms-and-changes', + 'energy-skate-park', + 'energy-skate-park'], + 'design_team': 'Ariel Paul (lead designer); Amy Rouinfar (lead designer); Sam Reid (lead ' + 'developer); Jesse Greenberg (lead developer); Marla Schulz (developer); ' + 'Agustín Vallejo (developer); Lusia Vargas (developer); Michael Dubson; ' + 'Bryce Gruneich; Trish Loeblein; Diana López Tavares; Emily B. Moore; Kathy ' + 'Perkins; Noah Podolefsky', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/energy-skate-park-basics', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/energy-skate-park-basics/latest/energy-skate-park-basics-600.png'}, + { 'slug': 'equality-explorer', + 'is_phet_studio': False, + 'title': 'Equality Explorer', + 'description': 'Explore what it means for a mathematical statement to be balanced or ' + 'unbalanced by interacting with objects on a balance. Discover the rules ' + 'for keeping it balanced. Collect stars by playing the game!', + 'learning_goals': 'Solve and manipulate algebraic equations by applying properties of real ' + 'numbers (in particular, the inverse properties of addition and ' + 'multiplication) and properties of equality
Solve and manipulate ' + 'algebraic equations by substituting different values for a variable ' + '
Use a balance model to solve an equation for an unknown, and ' + 'justify your strategies for solving
Solve an equation using only ' + 'universal operations', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Solving Equations', 'Inequalities', 'Inverse Operations'], + 'grades': ['middle', 'high'], + 'version': '1.2.5', + 'released': '2018-07-18', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lv', + 'mg', + 'mn', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'pr', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sn', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'wo', + 'xh', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': [ 'expression-exchange', + 'equality-explorer-basics', + 'equality-explorer-two-variables', + 'number-line-integers', + 'number-line-operations', + 'quadrilateral'], + 'design_team': 'Amanda McGarry (lead designer); Chris Malley (developer); Diana López ' + 'Tavares; Ariel Paul; Kathy Perkins; Argenta Price; Beth Stade; David Webb; ' + 'Mariah Hermsmeyer (graphic arts); Cheryl McCutchan (graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/equality-explorer', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/equality-explorer/latest/equality-explorer-600.png'}, + { 'slug': 'equality-explorer-basics', + 'is_phet_studio': False, + 'title': 'Equality Explorer: Basics', + 'description': 'Explore what it means for a mathematical statement to be balanced or ' + 'unbalanced by interacting with objects on a balance. Find all the ways to ' + 'balance cats and dogs or apples and oranges.', + 'learning_goals': 'Use a balance model to solve an equation for an unknown, and justify ' + 'your strategies for solving
Use proportional reasoning to determine ' + 'the value of a single object', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Proportional Reasoning', 'Inequalities', 'Equations'], + 'grades': ['elementary', 'middle'], + 'version': '1.2.5', + 'released': '2018-07-18', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lv', + 'mg', + 'mn', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'pr', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sn', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'xh', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': [ 'proportion-playground', + 'equality-explorer', + 'equality-explorer-two-variables', + 'ratio-and-proportion'], + 'design_team': 'Amanda McGarry (lead designer); Chris Malley (developer); Diana López ' + 'Tavares; Ariel Paul; Kathy Perkins; Argenta Price; Beth Stade; David Webb; ' + 'Mariah Hermsmeyer (graphic arts); Cheryl McCutchan (graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/equality-explorer-basics', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/equality-explorer-basics/latest/equality-explorer-basics-600.png'}, + { 'slug': 'equality-explorer-two-variables', + 'is_phet_studio': False, + 'title': 'Equality Explorer: Two Variables', + 'description': 'Explore what it means for a mathematical statement to be balanced or ' + 'unbalanced by interacting with integers and variables on a balance. Find ' + 'multiple ways to balance x and y to build a system of equations.', + 'learning_goals': 'Solve and manipulate algebraic equations by substituting different ' + 'values for a variable
Use a balance model to solve an equation for ' + 'an unknown, and justify your strategies for solving
Build a system ' + 'of equations
Determine the solution set of a system of equations', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Inequalities', 'Equations', 'System of Equations'], + 'grades': ['middle', 'high'], + 'version': '1.2.5', + 'released': '2018-07-18', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'gu', + 'ha', + 'hi', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lv', + 'mg', + 'mn', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'pr', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sn', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'xh', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['graphing-lines', 'equality-explorer', 'equality-explorer-basics'], + 'design_team': 'Amanda McGarry (lead designer); Chris Malley (developer); Diana López ' + 'Tavares; Ariel Paul; Kathy Perkins; Argenta Price; Beth Stade; David Webb; ' + 'Mariah Hermsmeyer (graphic arts); Cheryl McCutchan (graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/equality-explorer-two-variables', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/equality-explorer-two-variables/latest/equality-explorer-two-variables-600.png'}, + { 'slug': 'expression-exchange', + 'is_phet_studio': False, + 'title': 'Expression Exchange', + 'description': 'Build coin expressions, then exchange them for variable expressions. ' + 'Simplify and evaluate expressions until you are ready to test your ' + 'understanding of equivalent expressions in the game!', + 'learning_goals': 'Simplify expressions by combining like-terms
Contextualize ' + 'coefficients and like/unlike-terms
Interpret an expression in both ' + 'abstract and concrete representations
Build an expression that is ' + 'equivalent to a target', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': [ 'Simplifying Expressions', + 'Evaluating Expressions', + 'Equivalent Expressions'], + 'grades': ['elementary', 'middle', 'high'], + 'version': '1.1.33', + 'released': '2017-06-30', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lv', + 'mg', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': [ 'area-model-algebra', + 'equality-explorer', + 'number-line-integers', + 'number-line-operations'], + 'design_team': 'Amanda McGarry (lead designer); John Blanco (developer); David Webb; Kathy ' + 'Perkins; Ariel Paul', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/expression-exchange', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/expression-exchange/latest/expression-exchange-600.png'}, + { 'slug': 'faradays-electromagnetic-lab', + 'is_phet_studio': True, + 'title': "Faraday's Electromagnetic Lab", + 'description': "Experiment with magnets and coils to learn about Faraday's Law. Measure " + 'the direction and magnitude of the magnetic field. Induce a current ' + 'through the pickup coil to light a bulb and vary magnetic strength, number ' + 'of loops, and loop area. Explore applications of Faraday’s Law with ' + 'electromagnets, transformers, and generators.', + 'learning_goals': 'Predict the direction of the magnetic field for different locations ' + 'around a bar magnet and an electromagnet.
Compare and contrast bar ' + 'magnets and electromagnets.
Identify the characteristics of ' + 'electromagnets that are variable and what effects each variable has on ' + "the magnetic field's strength and direction.
Relate magnetic field " + 'strength to distance quantitatively and qualitatively.
Identify the ' + 'conditions that produce induction.
Compare and contrast how the ' + 'light bulb and voltmeter show characteristics of the induced ' + 'current.
Predict how the induced current will change when the ' + 'experimental conditions are varied.', + 'subjects': ['physics'], + 'topics': ['work-energy-and-power', 'electricity-magnets-and-circuits'], + 'detail_topics': ['Induction', "Faraday's Law", 'Electromotive Force'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.0.7', + 'released': '2024-07-30', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'be', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'is', + 'it', + 'iw', + 'km', + 'ko', + 'ku', + 'lt', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'sk', + 'sl', + 'sr', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tr', + 'uk', + 'vi', + 'yo', + 'zh_TW'], + 'related': [ 'faradays-law', + 'magnet-and-compass', + 'magnets-and-electromagnets', + 'generator'], + 'design_team': 'Chris Malley (developer); Amy Rouinfar (lead designer); Wendy Adams; Mike ' + 'Dubson; Danielle Harlow; Ariel Paul; Archie Paulson; Kathy Perkins; Carl ' + 'Wieman', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/faradays-electromagnetic-lab', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/faradays-electromagnetic-lab/latest/faradays-electromagnetic-lab-600.png'}, + { 'slug': 'faradays-law', + 'is_phet_studio': False, + 'title': "Faraday's Law", + 'description': "Investigate Faraday's law and how a changing magnetic flux can produce a " + 'flow of electricity!', + 'learning_goals': 'Explain what happens when the magnet moves through the coil at ' + 'different speeds and how this affects the brightness of the bulb and ' + 'the magnitude & sign of the voltage.
Explain the difference ' + 'between moving the magnet through the coil from the right side versus ' + 'the left side.
Explain the difference between moving magnet through ' + 'the big coil versus the smaller coil.', + 'subjects': ['physics'], + 'topics': ['electricity-magnets-and-circuits'], + 'detail_topics': ['Magnetic Field', "Faraday's Law", 'Magnets'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.4.25', + 'released': '2014-10-31', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lv', + 'mg', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'or', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'faradays-electromagnetic-lab', + 'magnet-and-compass', + 'magnets-and-electromagnets', + 'generator'], + 'design_team': 'Michael Dubson (lead); Bryce Gruneich (lead); John Blanco (developer); ' + 'Jonathan Olson (developer); Ariel Paul; Kathy Perkins; Amy Rouinfar', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/faradays-law', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/faradays-law/latest/faradays-law-600.png'}, + { 'slug': 'forces-and-motion-basics', + 'is_phet_studio': True, + 'title': 'Forces and Motion: Basics', + 'description': 'Explore the forces at work when pulling against a cart, and pushing a ' + 'refrigerator, crate, or person. Create an applied force and see how it ' + 'makes objects move. Change friction and see how it affects the motion of ' + 'objects.', + 'learning_goals': 'Identify when forces are balanced vs unbalanced.
Determine the sum ' + 'of forces (net force) on an object with more than one force on ' + 'it.
Predict the motion of an object with zero net ' + 'force.
Predict the direction of motion given a combination of ' + 'forces.', + 'subjects': ['physics'], + 'topics': ['motion'], + 'detail_topics': ['Motion', 'Friction', 'Force'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '2.6.5', + 'released': '2013-12-02', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mi', + 'mk', + 'ml', + 'mn', + 'mr', + 'ms', + 'mt', + 'my', + 'nb', + 'nl', + 'ny', + 'om', + 'or', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'ti', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['balancing-act', 'hookes-law', 'collision-lab'], + 'design_team': 'Noah Podolefsky (lead designer); Ariel Paul (lead designer); Sam Reid ' + '(lead developer); Jesse Greenberg (developer); Marla Schulz (developer); ' + 'Luisa Vargas (developer); Patricia Loeblein; Matthew Moore; Kathy Perkins; ' + 'Amy Rouinfar; Nancy Salpepi', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/forces-and-motion-basics', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/forces-and-motion-basics/latest/forces-and-motion-basics-600.png'}, + { 'slug': 'fourier-making-waves', + 'is_phet_studio': False, + 'title': 'Fourier: Making Waves', + 'description': 'Add up sines or cosines to make waves of different shapes. Discover how ' + 'changing the amplitudes of different harmonics changes the sound you hear. ' + 'Play the Wave Game and combine harmonics to match a target waveform. ' + 'Construct wave packets and explore the effects of changing the spacing ' + 'between Fourier components and wave packet width.', + 'learning_goals': 'Explain qualitatively how sines and cosines add up to produce arbitrary ' + 'periodic functions.
Recognize that each Fourier component ' + 'corresponds to a sinusoidal wave with a different wavelength or ' + 'period.
Describe sounds in terms of sinusoidal waves.
Compare ' + 'and contrast waves in space and waves in time.
Recognize that ' + 'wavelength and period do not correspond to specific points on the graph ' + 'but indicate the length/time between two consecutive troughs, peaks, or ' + 'any other corresponding points.
Relate the mathematical notation of ' + 'a Fourier series to its graphical representation and determine which ' + 'aspect of the graph is described by each of the symbols in the ' + 'equation.
Recognize that λ & T and k & ω are analogous, but not the ' + 'same.
Translate an equation from summation notation to expanded ' + 'notation.
Recognize that the width of a wave packet in position ' + 'space is inversely related to the width of a wave packet in Fourier ' + 'space.
Explain how the Heisenberg Uncertainty Principle results ' + 'from the properties of waves.
Recognize that the spacing between ' + 'Fourier components is inversely related to the spacing between wave ' + 'packets, and that a continuous distribution of Fourier components leads ' + 'to a single wave packet.', + 'subjects': ['physics', 'chemistry', 'math'], + 'topics': [ 'sound-and-waves', + 'quantum-phenomena', + 'light-and-radiation', + 'quantum-chemistry', + 'math-applications'], + 'detail_topics': ['Fourier Series', 'Uncertainty Principle', 'Wave Packets'], + 'grades': ['high', 'university'], + 'version': '1.1.5', + 'released': '2021-10-13', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'ig', + 'is', + 'it', + 'iw', + 'ki', + 'ko', + 'ku', + 'lg', + 'mn', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_TW', + 'zu'], + 'related': ['wave-interference'], + 'design_team': 'Chris Malley (lead developer); Amy Rouinfar (lead designer); Wendy Adams; ' + 'Mike Dubson; Danielle Harlow; Sam McKagan; Ariel Paul; Kathy Perkins; Carl ' + 'Wieman', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/fourier-making-waves', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/fourier-making-waves/latest/fourier-making-waves-600.png'}, + { 'slug': 'fraction-matcher', + 'is_phet_studio': False, + 'title': 'Fraction Matcher', + 'description': 'Match shapes and numbers to earn stars in this fractions game. Challenge ' + 'yourself on any level you like. Try to collect lots of stars!', + 'learning_goals': 'Find matching fractions using numbers and pictures
Make the same ' + 'fractions using different numbers
Match fractions in different ' + 'picture patterns
Compare fractions using numbers and patterns', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Fractions', 'Equivalent Fractions', 'Mixed Numbers'], + 'grades': ['elementary', 'middle', 'high'], + 'version': '1.2.21', + 'released': '2014-07-18', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mg', + 'mi', + 'ml', + 'mn', + 'mr', + 'ms', + 'mt', + 'my', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sn', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'xh', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['arithmetic', 'build-a-fraction', 'fractions-equality'], + 'design_team': 'Ariel Paul (lead); Sam Reid (developer); Karina Hensberry; Patricia ' + 'Loeblein; Kathy Perkins; Michael Dubson; Noah Podolefsky', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/fraction-matcher', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/fraction-matcher/latest/fraction-matcher-600.png'}, + { 'slug': 'fractions-equality', + 'is_phet_studio': False, + 'title': 'Fractions: Equality', + 'description': 'Build equivalent fractions with different denominators. Match shapes and ' + 'numbers to earn stars in the game. Challenge yourself on any level you ' + 'like. Try to collect lots of stars!', + 'learning_goals': 'Make equivalent fractions using different numbers
Match fractions ' + 'in different picture patterns
Compare fractions on a number line', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Equivalent Fractions', 'Improper Fraction', 'Number Line'], + 'grades': ['elementary', 'middle'], + 'version': '1.1.22', + 'released': '2019-01-29', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mg', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'my', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sn', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['fraction-matcher', 'fractions-intro'], + 'design_team': 'Ariel Paul (lead designer); Jonathan Olson (lead developer); Sam Reid ' + '(original developer); Martin Veillette (developer); Michael Dubsin; Trish ' + 'Loeblein; Amanda McGarry; Kathy Perkins; Vincent Davis; Michael Moorer; ' + 'Dusty Cole', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/fractions-equality', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/fractions-equality/latest/fractions-equality-600.png'}, + { 'slug': 'fractions-intro', + 'is_phet_studio': False, + 'title': 'Fractions: Intro', + 'description': 'Explore fractions while you help yourself to 1/3 of a chocolate cake and ' + 'wash it down with 1/2 a glass of water! Create your own fractions using ' + 'fun interactive objects. Match shapes and numbers to earn stars in the ' + 'fractions game. Challenge yourself on any level you like. Try to collect ' + 'lots of stars!', + 'learning_goals': 'Predict and explain how changing the numerator of a fraction affects ' + "the fraction's value
Predict and explain how changing the " + "denominator of a fraction affects the fraction's value
Convert " + 'between a picture of a fraction, a numeric fraction, and a point on a ' + 'number line
Build matching fractions using numbers and pictures ' + '
Compare fractions using numbers and patterns', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Fractions', 'Equivalent Fractions', 'Improper Fraction'], + 'grades': ['elementary', 'middle'], + 'version': '1.0.33', + 'released': '2019-01-29', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'ee', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lv', + 'mg', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'my', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'pr', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sn', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'wo', + 'xh', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['build-a-fraction', 'fractions-equality', 'fractions-mixed-numbers'], + 'design_team': 'Ariel Paul (lead designer); Jonathan Olson (lead developer); Sam Reid ' + '(original developer); Martin Veillette (developer); Michael Dubson; Trish ' + 'Loeblein; Amanda McGarry; Kathy Perkins; Vincent Davis ; Michael Moorer; ' + 'Dusty Cole', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/fractions-intro', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/fractions-intro/latest/fractions-intro-600.png'}, + { 'slug': 'fractions-mixed-numbers', + 'is_phet_studio': False, + 'title': 'Fractions: Mixed Numbers', + 'description': 'Explore fractions while you help yourself to 1 and 1/2 chocolate cakes and ' + 'wash it down with 1/3 a glass of water! Create your own fractions using ' + 'fun interactive objects. Match shapes and numbers to earn stars in the ' + 'mixed number game. Challenge yourself on any level you like. Try to ' + 'collect lots of stars!', + 'learning_goals': 'Predict and explain how changing the numerator of a fraction affects ' + "the fraction's value
Predict and explain how changing the " + "denominator of a fraction affects the fraction's value
Convert " + 'between a picture of a fraction, an improper fraction, and a mixed ' + 'number
Build matching fractions using numbers and pictures', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Fractions', 'Equivalent Fractions', 'Mixed Numbers'], + 'grades': ['elementary', 'middle'], + 'version': '1.0.33', + 'released': '2019-01-29', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mg', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'my', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sn', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'xh', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['build-a-fraction', 'fractions-intro'], + 'design_team': 'Amanda McGarry (lead designer); Jonathan Olson (lead developer); Sam Reid ' + '(original developer); Martin Veillette (developer); Michael Dubson; Trish ' + 'Loeblein; Ariel Paul; Kathy Perkins; Vincent Davis; Michael Moorer; Dusty ' + 'Cole', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/fractions-mixed-numbers', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/fractions-mixed-numbers/latest/fractions-mixed-numbers-600.png'}, + { 'slug': 'friction', + 'is_phet_studio': True, + 'title': 'Friction', + 'description': 'Move the Chemistry book and observe what happens.', + 'learning_goals': 'Describe a model for friction a molecular level.
Describe matter in ' + 'terms of molecular motion. The description should include: diagrams to ' + 'support the description, how the temperature affects the image, what ' + 'are the differences and similarities between solid, liquid and gas ' + 'particle motion; how the size and speed of gas molecules relate to ' + 'everyday objects.', + 'subjects': ['physics'], + 'topics': ['motion', 'heat-and-thermo'], + 'detail_topics': ['Thermodynamics', 'Friction', 'Heat'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.6.23', + 'released': '2014-01-07', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lo', + 'lt', + 'lv', + 'mi', + 'mk', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sn', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [], + 'design_team': 'Noah Podolefsky (lead); Michael Dubson (lead, developer); John Blanco ' + '(developer); Jonathan Olson (developer); Michael Kauzmann (developer); ' + 'Bryce Gruneich; Emily B. Moore; Matthew Moore; Ariel Paul; Kathy Perkins; ' + 'Taliesin Smith; Carl Wieman; Wendy Adams (interviewer); Mindy Gratny ' + '(interviewer); Ashton Morris (sound design); Mike Winters (sound design)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/friction', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/friction/latest/friction-600.png'}, + { 'slug': 'function-builder', + 'is_phet_studio': False, + 'title': 'Function Builder', + 'description': 'Play with functions while you ponder Art History. Explore geometric ' + 'transformations and transform your thinking about linear functions, then ' + 'have fun figuring out the mystery functions!', + 'learning_goals': 'Define a function as a rule relating each input to exactly one output ' + 'and predictably acting on inputs
Predict outputs of a function ' + 'using given inputs
Compose functions to create a new ' + 'function
Interpret, compare, and translate between multiple ' + 'representations of an algebraic function', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Linear Equation', 'Functions', 'Ordered Pairs'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.2.18', + 'released': '2016-07-13', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lo', + 'lv', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['graphing-lines', 'function-builder-basics', 'graphing-slope-intercept'], + 'design_team': 'Amanda McGarry (lead designer); Chris Malley (developer); Amy Hanson ; ' + 'Karina K. R. Hensberry ; Ariel Paul; Kathy Perkins ; Sam Reid; Beth Stade; ' + 'David Webb', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/function-builder', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/function-builder/latest/function-builder-600.png'}, + { 'slug': 'function-builder-basics', + 'is_phet_studio': False, + 'title': 'Function Builder: Basics', + 'description': 'Play with functions while you ponder Art History. Look for patterns, then ' + 'apply what you learned on the Mystery screen!', + 'learning_goals': 'Define a function as a rule relating each input to exactly one output ' + 'and predictably acting on inputs
Predict outputs of a function ' + 'using given inputs
Compose functions to create a new ' + 'function
Determine which functions are geometric transformations', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Functions', 'Ordered Pairs', 'Transformations'], + 'grades': ['elementary', 'middle'], + 'version': '1.2.19', + 'released': '2017-06-06', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fr', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lo', + 'lv', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['function-builder'], + 'design_team': 'Amanda McGarry (lead designer); Chris Malley (developer); Amy Hanson ; ' + 'Karina K. R. Hensberry ; Ariel Paul; Kathy Perkins ; Sam Reid; Beth Stade; ' + 'David Webb; Cheryl McCutchan (graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/function-builder-basics', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/function-builder-basics/latest/function-builder-basics-600.png'}, + { 'slug': 'gas-properties', + 'is_phet_studio': True, + 'title': 'Gas Properties', + 'description': 'Pump gas molecules to a box and see what happens as you change the volume, ' + 'add or remove heat, and more. Measure the temperature and pressure, and ' + 'discover how the properties of the gas vary in relation to each other. ' + 'Examine kinetic energy and speed histograms for light and heavy particles. ' + 'Explore diffusion and determine how concentration, temperature, mass, and ' + 'radius affect the rate of diffusion.', + 'learning_goals': 'Determine how changing a variable among P, V, N, and T influences other ' + 'gas properties.
Describe the relationship between particle-wall ' + 'collisions and pressure.
Predict how changing temperature will ' + 'affect the speed of molecules.
Predict the speed of molecules in ' + 'thermal equilibrium based on the relative masses of molecules. ' + '
Identify when pressure-volume work is done on or by a gas. ' + '
Explain how two gases mix.
Design an experiment to find the ' + 'factors which affect the rate of diffusion.', + 'subjects': ['physics', 'chemistry', 'earth-science'], + 'topics': ['work-energy-and-power', 'heat-and-thermo', 'general-chemistry'], + 'detail_topics': ['PV Work', 'Ideal Gas Law', 'Diffusion'], + 'grades': ['high', 'university'], + 'version': '1.1.10', + 'released': '2019-08-13', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sn', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tl', + 'tn', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['states-of-matter', 'states-of-matter-basics', 'diffusion', 'gases-intro'], + 'design_team': 'Chris Malley (developer); Amy Rouinfar (lead designer); Jack Barbera; John ' + 'Blanco; Michael Dubson; Amy Hanson; Linda Koch; Ron LeMaster; Trish ' + 'Loeblein; Emily B. Moore; Ariel Paul; Dennis Perepelitsa; Kathy Perkins; ' + 'Tom Perkins; Carl Wieman', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/gas-properties', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/gas-properties/latest/gas-properties-600.png'}, + { 'slug': 'gases-intro', + 'is_phet_studio': True, + 'title': 'Gases Intro', + 'description': 'Pump gas molecules to a box and see what happens as you change the volume, ' + 'add or remove heat, and more. Measure the temperature and pressure, and ' + 'discover how the properties of the gas vary in relation to each other.', + 'learning_goals': 'Describe the behavior of the gas particles in the box.
Identify the ' + 'relationship between pressure, volume, temperature, and number of gas ' + 'molecules.
Describe the relationship between particle-wall ' + 'collisions and pressure.
Predict how changing temperature will ' + 'affect the speed of molecules.', + 'subjects': ['physics', 'chemistry', 'earth-science'], + 'topics': ['heat-and-thermo', 'general-chemistry'], + 'detail_topics': ['Pressure', 'Temperature', 'Volume'], + 'grades': ['elementary', 'middle'], + 'version': '1.1.7', + 'released': '2019-08-13', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tl', + 'tn', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['states-of-matter-basics', 'gas-properties', 'diffusion'], + 'design_team': 'Chris Malley (developer); Amy Rouinfar (lead designer); Jack Barbera; John ' + 'Blanco; Michael Dubson; Amy Hanson; Linda Koch; Ron LeMaster; Trish ' + 'Loeblein; Emily B. Moore; Ariel Paul; Dennis Perepelitsa; Kathy Perkins; ' + 'Tom Perkins; Carl Wieman', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/gases-intro', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/gases-intro/latest/gases-intro-600.png'}, + { 'slug': 'gene-expression-essentials', + 'is_phet_studio': False, + 'title': 'Gene Expression Essentials', + 'description': 'Express yourself through your genes! See if you can generate and collect ' + 'three types of protein, then move on to explore the factors that affect ' + 'protein synthesis in a cell.', + 'learning_goals': 'Explain the main sequence of events that occur within a cell that leads ' + 'to protein synthesis.
Predict how changing the concentrations and ' + 'interactions of biomolecules affects protein production.
Explain ' + 'how protein production in a single cell relates to the quantity ' + 'produced by a collection of cells.', + 'subjects': ['biology'], + 'topics': [], + 'detail_topics': ['Cells', 'Gene Expression', 'DNA Transcription'], + 'grades': ['high', 'university'], + 'version': '1.0.36', + 'released': '2017-12-22', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fr', + 'gu', + 'ha', + 'hi', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mk', + 'mn', + 'ms', + 'mt', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['natural-selection'], + 'design_team': 'Steele Dalton (lead); John Blanco (co-lead, software development); Aadish ' + 'Gupta (software development); Sharfudeen Ashraf (software development); ' + 'Mike Klymkowsky; Kathy Perkins; Ariel Paul (design, interviews); Tom ' + 'Perkins; Amanda McGarry; George Emanuel (multi-cell model software)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/gene-expression-essentials', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/gene-expression-essentials/latest/gene-expression-essentials-600.png'}, + { 'slug': 'generator', + 'is_phet_studio': True, + 'title': 'Generator', + 'description': 'Generate electricity with a spinning bar magnet. Explore the underlying ' + 'physics of a generator and discover how to maximize the brightness of a ' + 'light bulb.', + 'learning_goals': 'Identify the conditions that produce induction.
Compare and ' + 'contrast how the light bulb and voltmeter show characteristics of the ' + 'induced current.
Predict how the induced current will change when ' + 'the experimental conditions are varied.
Explain the cause of ' + 'induction.', + 'subjects': ['physics'], + 'topics': ['work-energy-and-power', 'electricity-magnets-and-circuits'], + 'detail_topics': ['Electricity', 'Induction', 'Magnetic Field'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.0.7', + 'released': '2024-07-30', + 'updated': '2026-06-24', + 'is_new': True, + 'locales': [ 'am', + 'ar', + 'ar_LY', + 'be', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'is', + 'it', + 'iw', + 'km', + 'ko', + 'ku', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'om', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'sk', + 'sl', + 'sr', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tr', + 'uk', + 'vi', + 'yo', + 'zh_TW'], + 'related': [ 'faradays-law', + 'faradays-electromagnetic-lab', + 'magnet-and-compass', + 'magnets-and-electromagnets'], + 'design_team': 'Chris Malley (developer); Amy Rouinfar (lead designer); Wendy Adams; Mike ' + 'Dubson; Danielle Harlow; Ariel Paul; Archie Paulson; Kathy Perkins; Carl ' + 'Wieman', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/generator', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/generator/latest/generator-600.png'}, + { 'slug': 'geometric-optics', + 'is_phet_studio': True, + 'title': 'Geometric Optics', + 'description': 'How does a lens or mirror form an image? See how light rays are refracted ' + 'by a lens or reflected by a mirror. Observe how the image changes when you ' + 'adjust the focal length of the lens, move the object, or move the screen.', + 'learning_goals': 'Explain how an image is formed by a converging or diverging lens or ' + 'mirror using ray diagrams.
Determine how changing the parameters of ' + 'the optic (radius of curvature, index of refraction) affects where the ' + 'image is formed and how it appears (magnification, brightness, and ' + 'inversion).
Predict where an image will be formed given the object ' + 'distance and optic parameters.', + 'subjects': ['physics'], + 'topics': ['light-and-radiation'], + 'detail_topics': ['Optics', 'Focal Length', 'Lenses'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.3.16', + 'released': '2022-04-06', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['bending-light', 'geometric-optics-basics'], + 'design_team': 'Sarah Chang (developer); Chris Malley (developer); Martin Veillette ' + '(developer); Amy Rouinfar (lead designer); Mike Dubson; Chris Klusendorf; ' + 'Diana López Tavares; Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/geometric-optics', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/geometric-optics/latest/geometric-optics-600.png'}, + { 'slug': 'geometric-optics-basics', + 'is_phet_studio': True, + 'title': 'Geometric Optics: Basics', + 'description': 'How does a lens or mirror form an image? See how light rays are refracted ' + 'by a lens or reflected by a mirror. Observe how the image changes when you ' + 'adjust the focal length, move the object, or move the screen.', + 'learning_goals': 'Explain how an image is formed by a converging lens or flat mirror ' + 'using ray diagrams.
Determine how changing the parameters of a lens ' + '(focal length, diameter) affects where the image is formed and how it ' + 'appears (magnification, brightness, and inversion).
Predict where ' + 'an image will be formed given the object distance and optic parameters.', + 'subjects': ['physics'], + 'topics': ['light-and-radiation'], + 'detail_topics': ['Optics', 'Focal Length', 'Lenses'], + 'grades': ['elementary', 'middle', 'high'], + 'version': '1.3.16', + 'released': '2022-11-30', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'ar', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'sk', + 'sl', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'th', + 'tr', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_TW', + 'zu'], + 'related': ['bending-light', 'geometric-optics'], + 'design_team': 'Sarah Chang (developer); Chris Malley (developer); Martin Veillette ' + '(developer); Amy Rouinfar (lead designer); Mike Dubson; Chris Klusendorf; ' + 'Diana López Tavares; Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/geometric-optics-basics', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/geometric-optics-basics/latest/geometric-optics-basics-600.png'}, + { 'slug': 'graphing-lines', + 'is_phet_studio': False, + 'title': 'Graphing Lines', + 'description': 'Explore the world of lines. Investigate the relationships between linear ' + 'equations, slope, and graphs of lines. Challenge yourself in the line ' + 'game!', + 'learning_goals': 'Explain how the slope of a graphed line can be computed.
Graph a ' + 'line given an equation in either slope-intercept or point-slope ' + 'form.
Write an equation in slope-intercept or point-slope form ' + 'given a graphed line.
Predict how changing variables in a linear ' + 'equation will affect the graphed line.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Graphing Linear Equations', 'Lines', 'Slope'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.4.8', + 'released': '2014-07-21', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lo', + 'lv', + 'mg', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': [ 'least-squares-regression', + 'function-builder', + 'graphing-slope-intercept', + 'equality-explorer-two-variables', + 'graphing-quadratics', + 'number-line-distance'], + 'design_team': 'Ariel Paul (lead); Chris Malley (developer); Bryce Gruneich; Karina ' + 'Hensberry; Patricia Loeblein; Amanda McGarry; Kathy Perkins; Sharon ' + 'Siman-Tov (graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/graphing-lines', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/graphing-lines/latest/graphing-lines-600.png'}, + { 'slug': 'graphing-quadratics', + 'is_phet_studio': True, + 'title': 'Graphing Quadratics', + 'description': 'Discover how changing coefficients changes the shape of a curve. View the ' + 'graphs of individual terms (e.g. y=bx) to see how they add to generate the ' + 'polynomial curve. Generate definitions for vertex, roots, and axis of ' + 'symmetry. Compare different forms of a quadratic function. Define a curve ' + 'by its focus and directrix.', + 'learning_goals': 'Describe how changing the coefficients of a quadratic function changes ' + 'the graph of the function.
Predict how the graph of a parabola will ' + 'change if the coefficients or constant are varied.
Identify the ' + 'vertex, axis of symmetry, roots, and directrix for the graph of a ' + 'quadratic equation.
Use the vertex form of a quadratic function to ' + 'describe the graph of the function.
Describe the relationship ' + 'between the focus and directrix and resulting parabola.
Predict ' + 'the graph of a parabola given a focus and directrix.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Graphing', 'Parabola', 'Vertex'], + 'grades': ['middle', 'high'], + 'version': '1.4.4', + 'released': '2018-12-08', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lo', + 'lv', + 'mi', + 'mn', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['graphing-lines'], + 'design_team': 'Chris Malley (developer); Andrea Lin (developer); Amanda McGarry (lead ' + 'designer); Michael Dubson (original designer); Catherine Carter; Karina K. ' + 'R. Hensberry; Trish Loeblein; Ariel Paul; Kathy Perkins; Taliesin Smith', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/graphing-quadratics', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/graphing-quadratics/latest/graphing-quadratics-600.png'}, + { 'slug': 'graphing-slope-intercept', + 'is_phet_studio': False, + 'title': 'Graphing Slope-Intercept', + 'description': 'Explore slope-intercept form of a line. Connect the slope and y-intercept ' + 'to the equation of the line. Challenge yourself in the line game!', + 'learning_goals': 'Graph a line given an equation in slope-intercept form.
Write an ' + 'equation in slope-intercept form given a graphed line.
Predict how ' + 'changing the values in a linear equation will affect the graphed ' + 'line.
Predict how changing the graphed line will affect the ' + 'equation.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Graphing Linear Equations', 'Slope-Intercept Form'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.2.8', + 'released': '2017-06-14', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lo', + 'lv', + 'mg', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['least-squares-regression', 'graphing-lines', 'function-builder'], + 'design_team': 'Amanda McGarry (lead); Ariel Paul (lead); Chris Malley (developer); Bryce ' + 'Gruneich; Karina Hensberry; Patricia Loeblein; Kathy Perkins; Sharon ' + 'Siman-Tov (graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/graphing-slope-intercept', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/graphing-slope-intercept/latest/graphing-slope-intercept-600.png'}, + { 'slug': 'gravity-force-lab', + 'is_phet_studio': False, + 'title': 'Gravity Force Lab', + 'description': 'Visualize the gravitational force that two objects exert on each other. ' + 'Adjust properties of the objects to see how changing the properties ' + 'affects the gravitational attraction.', + 'learning_goals': 'Relate gravitational force to masses of objects and distance between ' + "objects.
Explain Newton's third law for gravitational " + 'forces.
Design experiments that allow you to derive an equation ' + 'that relates mass, distance, and gravitational force.
Use ' + 'measurements to determine the universal gravitational constant.', + 'subjects': ['physics', 'earth-science'], + 'topics': ['motion'], + 'detail_topics': ['Gravitational Force', "Newton's Third Law", 'Force Pairs'], + 'grades': ['high', 'university'], + 'version': '2.2.25', + 'released': '2013-08-23', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_CO', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mi', + 'mk', + 'ml', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['coulombs-law', 'gravity-force-lab-basics', 'my-solar-system', 'keplers-laws'], + 'design_team': 'Michael Barlow (developer); Sam Reid (developer); John Blaco (developer); ' + 'Aadish Gupta (developer); Michael Kauzmann (developer); Sam Reid ' + '(developer); Wendy Adams; Patricia Loeblein; Emily B. Moore; Ariel Paul; ' + 'Noah Podolefsky; Amy Rouinfar; Taliesin Smith; Brianna Tomlinson; Carl ' + 'Wieman; Mariah Hermsmeyer (graphic arts); Ashton Morris (sound design)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/gravity-force-lab', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/gravity-force-lab/latest/gravity-force-lab-600.png'}, + { 'slug': 'gravity-force-lab-basics', + 'is_phet_studio': False, + 'title': 'Gravity Force Lab: Basics', + 'description': 'Visualize the gravitational force that two objects exert on each other. ' + 'Discover the factors that affect gravitational attraction, and determine ' + 'how adjusting these factors will change the gravitational force.', + 'learning_goals': 'Relate the gravitational force to masses of objects and distance ' + "between objects.
Explain Newton's third law for gravitational " + 'forces.
Use measurements to determine the universal gravitational ' + 'constant.', + 'subjects': ['physics', 'earth-science'], + 'topics': ['motion'], + 'detail_topics': ['Gravitational Force', "Newton's Third Law", 'Force Pairs'], + 'grades': ['elementary', 'middle'], + 'version': '1.1.27', + 'released': '2019-10-25', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'kk', + 'km', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['gravity-force-lab', 'gravity-and-orbits', 'coulombs-law', 'my-solar-system'], + 'design_team': 'Michael Barlow (developer); John Blanco (developer); Jesse Greenberg ' + '(developer); Michael Kauzmann (developer); Amy Rouinfar (lead designer); ' + 'Ariel Paul; Kathy Perkins; Emily B. Moore; Taliesin Smith; Brianna ' + 'Tomlinson; Mariah Hermsmeyer (graphic arts); Ashton Morris (sound design)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/gravity-force-lab-basics', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/gravity-force-lab-basics/latest/gravity-force-lab-basics-600.png'}, + { 'slug': 'gravity-and-orbits', + 'is_phet_studio': True, + 'title': 'Gravity and Orbits', + 'description': 'Move the sun, earth, moon and space station to see how it affects their ' + 'gravitational forces and orbital paths. Visualize the sizes and distances ' + 'between different heavenly bodies, and turn off gravity to see what would ' + 'happen without it!', + 'learning_goals': 'Describe the relationship between the Sun, Earth, Moon and space ' + 'station, including orbits and positions
Describe the size and ' + 'distance between the Sun, Earth, Moon and space station
Explain how ' + 'gravity controls the motion of our solar system
Identify the ' + 'variables that affect the strength of gravity
Predict how motion ' + 'would change if gravity was stronger or weaker', + 'subjects': ['physics', 'earth-science'], + 'topics': ['motion'], + 'detail_topics': ['Gravitational Force', 'Circular Motion', 'Astronomy'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.6.28', + 'released': '2016-11-28', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mi', + 'mk', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'or', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['gravity-force-lab-basics', 'my-solar-system', 'keplers-laws'], + 'design_team': 'Amy Rouinfar (lead designer); Emily Moore (lead designer); Noah Podolefsky ' + '(lead designer); Trish Loeblein; Ariel Paul; Kathy Perkins; Jesse ' + 'Greenberg (lead developer); Sam Reid (lead developer); Aaron Davis ' + '(developer); Jon Olson (developer)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/gravity-and-orbits', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/gravity-and-orbits/latest/gravity-and-orbits-600.png'}, + { 'slug': 'greenhouse-effect', + 'is_phet_studio': True, + 'title': 'Greenhouse Effect', + 'description': 'How do greenhouse gases affect the climate? Explore the atmosphere during ' + 'the ice age and today. What happens when you add clouds? Change the ' + 'greenhouse gas concentration and see how the temperature changes.', + 'learning_goals': 'Describe the effect of greenhouse gases and clouds on sunlight, ' + 'infrared radiation, and surface temperature.
Explain why greenhouse ' + 'gases affect the temperature.
Compare and contrast the behavior of ' + 'sunlight and infrared radiation.
Describe radiative balance and use ' + 'it to explain the relationship between the surface temperature and ' + 'greenhouse gas concentration.
Compare the effect of greenhouse ' + 'gases to the effect of infrared absorbing layers.', + 'subjects': ['earth-science'], + 'topics': [], + 'detail_topics': ['Climate', 'Thermal Equilibrium', 'Greenhouse Gases'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.3.13', + 'released': '2023-02-17', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lt', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'ti', + 'tk', + 'tr', + 'tw', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['molecules-and-light', 'energy-forms-and-changes', 'blackbody-spectrum'], + 'design_team': 'Amy Rouinfar (lead designer); John Blanco (developer); Jesse Greenberg ' + '(developer); Wendy Adams; Anne U. Gold; Wanda Díaz Merced; Danielle ' + 'Harlow; Kelly Lancaster; Trish Loeblein; Matthew Moore; Mark Nielsen; ' + 'Ariel Paul; Robert Parson; Kathy Perkins; Taliesin Smith; Carl Wieman; ' + 'Mariah Hermsmeyer (graphic arts); Ashton Morris (sound design)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/greenhouse-effect', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/greenhouse-effect/latest/greenhouse-effect-600.png'}, + { 'slug': 'hookes-law', + 'is_phet_studio': True, + 'title': "Hooke's Law", + 'description': 'Stretch and compress springs to explore the relationships between force, ' + 'spring constant, displacement, and potential energy! Investigate what ' + 'happens when two springs are connected in series and parallel.', + 'learning_goals': 'Explain the relationships between applied force, spring force, spring ' + 'constant, displacement, and potential energy.
Describe how ' + 'connecting two springs in series or parallel affects the effective ' + 'spring constant and the spring forces.
Predict how the potential ' + 'energy stored in the spring changes as the spring constant and ' + 'displacement change.', + 'subjects': ['physics'], + 'topics': ['motion', 'work-energy-and-power'], + 'detail_topics': ['Vectors', 'Potential Energy', 'Force'], + 'grades': ['high', 'university'], + 'version': '1.2.4', + 'released': '2015-10-21', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'ti', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'energy-skate-park-basics', + 'forces-and-motion-basics', + 'masses-and-springs', + 'masses-and-springs-basics', + 'energy-skate-park'], + 'design_team': 'Amy Rouinfar (lead); Chris Malley (developer); Mike Dubson; Bruna ' + 'Shinohara de Mendonça; Ariel Paul; Kathy Perkins; Nancy Salpepi; Martin ' + 'Veillette', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/hookes-law', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/hookes-law/latest/hookes-law-600.png'}, + { 'slug': 'isotopes-and-atomic-mass', + 'is_phet_studio': False, + 'title': 'Isotopes and Atomic Mass', + 'description': 'Are all atoms of an element the same? How can you tell one isotope from ' + 'another? Use the sim to learn about isotopes and how abundance relates to ' + 'the average atomic mass of an element.', + 'learning_goals': 'Define “isotope” using mass number, atomic number, number of protons, ' + 'neutrons and electrons.
Given information about an element, find ' + 'the mass and name of an isotope.
Give evidence to support or ' + 'dispute: “In nature, the chance of finding one isotope of an element is ' + 'the same for all isotopes.”
Find the average atomic mass of an ' + 'element given the abundance and mass of its isotopes.
Predict how ' + 'the mass and name of an isotope will change given a change in the ' + 'number of protons, neutrons or electrons.
Predict how the average ' + 'atomic mass of an element changes given a change in the abundance of ' + 'its isotopes.', + 'subjects': ['chemistry'], + 'topics': ['general-chemistry'], + 'detail_topics': ['Isotopes', 'Atomic Mass'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.2.1', + 'released': '2016-05-24', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'or', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sn', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['build-an-atom', 'build-a-nucleus'], + 'design_team': 'Amy Hanson (lead designer - HTML5) ; Kelly Lancaster (lead designer - ' + 'Java); John Blanco (developer); Jesse Greenberg (developer); Aadish Gupta ' + '(developer); James Smith (developer); Sam Reid (developer); Trish ' + 'Loeblein; Emily Moore; Robert Parson; Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/isotopes-and-atomic-mass', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/isotopes-and-atomic-mass/latest/isotopes-and-atomic-mass-600.png'}, + { 'slug': 'john-travoltage', + 'is_phet_studio': False, + 'title': 'John Travoltage', + 'description': 'Play with John’s foot and arm to explore when he gets a shock!', + 'learning_goals': 'Describe and draw models for common static electricity concepts ' + '(transfer of charge, attraction, repulsion, and grounding).', + 'subjects': ['physics'], + 'topics': ['electricity-magnets-and-circuits'], + 'detail_topics': ['Static Electricity'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.6.26', + 'released': '2013-11-20', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mk', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['balloons-and-static-electricity'], + 'design_team': 'Noah Podolefsky (lead); Carl Wieman (lead); Sam Reid (lead, developer); ' + 'Jesse Greenberg; Trish Loeblein; Emily B. Moore; Ariel Paul; Kathy ' + 'Perkins; Taliesin Smith; Jonathan Hung (Inclusive Design Research Centre); ' + 'Justin Obara (Inclusive Design Research Centre); Sharon Siman-Tov (graphic ' + 'arts); Maria Hermsmeyer (graphic arts); Wendy Adams (interviewer); Ashton ' + 'Morris (sound design); Mike Winters (sound design)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/john-travoltage', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/john-travoltage/latest/john-travoltage-600.png'}, + { 'slug': 'keplers-laws', + 'is_phet_studio': True, + 'title': "Kepler's Laws", + 'description': 'Interact with the variables to discover how planetary objects moves in ' + 'elliptical orbits, and the other characteristics of these orbits described ' + 'by the three Kepler’s Laws. Connect Astronomy with Math, by experimenting ' + 'with ellipses, areas, and graphs.', + 'learning_goals': 'Explore how the velocity and position of a planet affect its movement ' + 'and orbit.
Discover how Kepler’s Laws apply for different bodies ' + 'in the solar system
Describe the characteristics of an ellipse ' + 'that support the understanding of planets’ orbits according to Kepler’s ' + 'first law
Visualize what is meant by “swept area of a planet’s ' + 'orbit” and its relationship with equal time intervals in the context of ' + "Kepler’s second Law.
Describe the behavior of the planet's " + 'velocity in different moments of its orbit
Explore the ' + 'relationship between the semi-major axis and the period of an orbit, ' + 'and their corresponding powers described by the Kepler’s third Law', + 'subjects': ['physics', 'earth-science', 'math'], + 'topics': ['motion', 'math-applications'], + 'detail_topics': ['Gravitational Force', 'Astronomy', 'Orbits'], + 'grades': ['high', 'university'], + 'version': '1.2.12', + 'released': '2023-10-04', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'ar', + 'az', + 'be', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'fa', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'is', + 'it', + 'iw', + 'ka', + 'ko', + 'ku', + 'mn', + 'ms', + 'mt', + 'nb', + 'nl', + 'om', + 'pl', + 'pt_BR', + 'ro', + 'sk', + 'sl', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tr', + 'uz', + 'vi', + 'zh_TW', + 'zu'], + 'related': ['gravity-force-lab', 'gravity-and-orbits', 'my-solar-system'], + 'design_team': 'Diana López Tavares (lead designer); Agustín Vallejo (lead developer); ' + 'Jonathan Olson (developer); Ashton Morris (sound design); Chris Malley; ' + 'Emily B. Moore; Ariel Paul; Kathy Perkins; Amy Rouinfar', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/keplers-laws', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/keplers-laws/latest/keplers-laws-600.png'}, + { 'slug': 'least-squares-regression', + 'is_phet_studio': False, + 'title': 'Least-Squares Regression', + 'description': 'Create your own scatter plot or use real-world data and try to fit a line ' + 'to it! Explore how individual data points affect the correlation ' + 'coefficient and best-fit line.', + 'learning_goals': 'Interpret r (the correlation coefficient) as data points are added, ' + 'moved, or removed.
Interpret the sum of the squared residuals ' + 'while manually fitting a line.
Interpret the sum of the squared ' + 'residuals of a best-fit line as a data point is added, moved, or ' + 'removed.
Compare the sum of the squared residuals between a ' + 'manually fitted line and the best-fit line.
Determine if a linear ' + 'fit is appropriate.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Correlation', 'Data', 'Linear Regression'], + 'grades': ['high', 'university'], + 'version': '1.2.5', + 'released': '2015-03-30', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lv', + 'mn', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tr', + 'tw', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'graphing-lines', + 'plinko-probability', + 'graphing-slope-intercept', + 'curve-fitting'], + 'design_team': 'Amanda McGarry (lead) ; Martin Veillette (developer) ; Patricia Loeblein; ' + 'Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/least-squares-regression', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/least-squares-regression/latest/least-squares-regression-600.png'}, + { 'slug': 'magnet-and-compass', + 'is_phet_studio': True, + 'title': 'Magnet and Compass', + 'description': 'Explore the interactions between a compass and bar magnet. Relate the ' + "Earth’s magnetic field to a bar magnet. Vary the magnet's strength, and " + 'see how things change both inside and outside. Measure the direction and ' + 'magnitude of the magnetic field.', + 'learning_goals': 'Predict the direction of the magnetic field for different locations ' + 'around a bar magnet and an electromagnet.
Relate magnetic field ' + 'strength to distance quantitatively and qualitatively.
Describe how ' + "the earth's magnetic field relates to a bar magnet.", + 'subjects': ['physics', 'earth-science'], + 'topics': ['electricity-magnets-and-circuits'], + 'detail_topics': ['Magnetism', 'Magnetic Field', 'Magnets'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.0.7', + 'released': '2024-07-30', + 'updated': '2026-06-24', + 'is_new': True, + 'locales': [ 'ar', + 'ar_LY', + 'be', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'fa', + 'fa_DA', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'is', + 'it', + 'iw', + 'km', + 'ko', + 'ku', + 'lt', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'om', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'sk', + 'sl', + 'sr', + 'sv', + 'sw', + 'te', + 'th', + 'tr', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW'], + 'related': [ 'faradays-law', + 'faradays-electromagnetic-lab', + 'magnets-and-electromagnets', + 'generator'], + 'design_team': 'Chris Malley (developer); Amy Rouinfar (lead designer); Wendy Adams; Mike ' + 'Dubson; Danielle Harlow; Ariel Paul; Archie Paulson; Kathy Perkins; Carl ' + 'Wieman', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/magnet-and-compass', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/magnet-and-compass/latest/magnet-and-compass-600.png'}, + { 'slug': 'magnets-and-electromagnets', + 'is_phet_studio': True, + 'title': 'Magnets and Electromagnets', + 'description': 'Explore the interactions between a compass and bar magnet. Discover how ' + 'you can use a battery and coil to make an electromagnet. Explore the ways ' + 'to change the magnetic field, and measure its direction and magnitude ' + 'around the magnet.', + 'learning_goals': 'Predict the direction of the magnetic field for different locations ' + 'around a bar magnet and an electromagnet.
Relate magnetic field ' + 'strength to distance quantitatively and qualitatively.
Describe how ' + "the earth's magnetic field relates to a bar magnet.
Compare and " + 'contrast bar magnets and electromagnets.
Identify the ' + 'characteristics of electromagnets that are variable and what effects ' + "each variable has on the magnetic field's strength and direction.", + 'subjects': ['physics'], + 'topics': ['electricity-magnets-and-circuits'], + 'detail_topics': ['Magnetic Field', 'Electromagnets', 'Magnets'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.0.7', + 'released': '2024-07-30', + 'updated': '2026-06-24', + 'is_new': True, + 'locales': [ 'ar', + 'ar_LY', + 'be', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'fa', + 'fa_DA', + 'fr', + 'ha', + 'hi', + 'hr', + 'ht', + 'is', + 'it', + 'iw', + 'km', + 'ko', + 'ku', + 'lt', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'om', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'sk', + 'sl', + 'sr', + 'sv', + 'sw', + 'te', + 'th', + 'tr', + 'vi', + 'yo', + 'zh_TW'], + 'related': [ 'faradays-law', + 'faradays-electromagnetic-lab', + 'magnet-and-compass', + 'generator'], + 'design_team': 'Chris Malley (developer); Amy Rouinfar (lead designer); Wendy Adams; Mike ' + 'Dubson; Danielle Harlow; Ariel Paul; Archie Paulson; Kathy Perkins; Carl ' + 'Wieman', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/magnets-and-electromagnets', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/magnets-and-electromagnets/latest/magnets-and-electromagnets-600.png'}, + { 'slug': 'make-a-ten', + 'is_phet_studio': False, + 'title': 'Make a Ten', + 'description': 'Add numbers by making tens. Break apart and combine numbers while focusing ' + 'on place value. Use the adding screen to add any two numbers. Use the game ' + 'screen to apply your make-a-ten strategies!', + 'learning_goals': 'Improve understanding of place value by using different sizes of papers ' + 'for ones, tens, and hundreds.
Develop mental math strategies when ' + 'taking apart and putting together numbers.
Use the “make a ten” ' + 'strategy when counting and doing addition.
Develop a mental model ' + 'of the basic properties of numbers including commutativity, ' + 'associativity, and closure.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Arithmetic', 'Addition', 'Subtraction'], + 'grades': ['elementary'], + 'version': '1.1.0', + 'released': '2017-03-01', + 'updated': '2026-08-06', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lv', + 'mg', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'pr', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['arithmetic', 'number-play', 'number-compare', 'number-pairs'], + 'design_team': 'Beth Stade (lead designer); Amanda McGarry (co-lead designer); Jonathan ' + 'Olson (developer); Sharfudeen Ashraf (developer); Sam Reid (developer); ' + 'Catherine Carter; Ariel Paul; Kathy Perkins; Mariah Hermsmeyer (graphic ' + 'arts); Afton Carter (sound)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/make-a-ten', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/make-a-ten/latest/make-a-ten-600.png'}, + { 'slug': 'masses-and-springs', + 'is_phet_studio': False, + 'title': 'Masses and Springs', + 'description': 'Hang masses from springs and adjust the spring constant and damping. ' + 'Transport the lab to different planets, or slow down time. Observe the ' + 'forces and energy in the system in real-time, and measure the period using ' + 'the stopwatch.', + 'learning_goals': 'Determine the factors which affect the period of oscillation
Find ' + 'the value of g on Planet X
Design an experiment to determine the ' + 'mass of an unknown object
Describe the relationship between the ' + 'velocity and acceleration vectors, and their relationship to motion, at ' + 'various points in the oscillation
Explain how the free-body diagram ' + 'of the mass changes throughout its oscillation
Explain the ' + 'Conservation of Mechanical Energy using kinetic, elastic potential, ' + 'gravitational potential, and thermal energy', + 'subjects': ['physics', 'math'], + 'topics': ['motion', 'work-energy-and-power', 'math-applications'], + 'detail_topics': ['Vectors', 'Conservation of Energy', "Newton's Laws"], + 'grades': ['high', 'university'], + 'version': '1.0.29', + 'released': '2018-09-20', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'energy-skate-park-basics', + 'hookes-law', + 'pendulum-lab', + 'masses-and-springs-basics', + 'energy-skate-park'], + 'design_team': 'Denzell Barnett (developer); Matt Pennington (developer); Amy Rouinfar ' + '(lead designer); Mike Dubson (original designer); Wendy Adams; Ariel Paul; ' + 'Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/masses-and-springs', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/masses-and-springs/latest/masses-and-springs-600.png'}, + { 'slug': 'masses-and-springs-basics', + 'is_phet_studio': False, + 'title': 'Masses and Springs: Basics', + 'description': 'Hang masses from springs and discover how they stretch and oscillate. ' + 'Compare two mass-spring systems, and experiment with spring constant. ' + 'Transport the lab to different planets, slow down time, and observe the ' + 'velocity and acceleration throughout the oscillation.', + 'learning_goals': 'Describe the natural length and equilibrium position in your own ' + 'words.
Find the relationship between the mass, spring strength ' + '(spring constant), and stretch (displacement).
Design an experiment ' + 'to determine the mass of a mystery object
Develop a method to ' + 'measure the period.
Determine the factors which affect the period ' + 'of oscillation.
Compare the gravity on Planet X to known ' + 'benchmarks.
Describe the relationship between the velocity and ' + 'acceleration vectors, and their relationship to motion, at various ' + 'points in the oscillation.', + 'subjects': ['physics'], + 'topics': ['motion'], + 'detail_topics': ["Hooke's Law", 'Measurement', 'Periodic Motion'], + 'grades': ['elementary', 'middle'], + 'version': '1.0.28', + 'released': '2019-03-22', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mi', + 'mn', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['hookes-law', 'pendulum-lab', 'masses-and-springs'], + 'design_team': 'Denzell Barnett (developer); Amy Rouinfar (lead designer); Mike Dubson ' + '(original designer); Wendy Adams; Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/masses-and-springs-basics', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/masses-and-springs-basics/latest/masses-and-springs-basics-600.png'}, + { 'slug': 'mean-share-and-balance', + 'is_phet_studio': True, + 'title': 'Mean: Share and Balance', + 'description': 'Explore the mean by leveling cups of water, distributing candy bars, ' + 'fairly sharing apples, and finding the balance point of a beam. Build a ' + 'data set with a target mean. Challenge yourself to predict the mean before ' + 'revealing it.', + 'learning_goals': 'Justify how the mean is calculated.
Describe the mean in terms of ' + 'leveling, fair share, and balance point.
Predict the effects of ' + '(an) outlier(s) on the mean.
Compare an original data set with ' + 'manipulated data to see how the mean (leveled out, shared, or balanced) ' + 'compares with the original data.
Predict the mean of a data set in ' + 'continuous and discrete contexts.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Central Tendency', 'Mean'], + 'grades': ['middle'], + 'version': '1.1.9', + 'released': '2024-08-28', + 'updated': '2026-06-24', + 'is_new': True, + 'locales': [ 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'az', + 'bg', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ko', + 'ku', + 'lg', + 'lt', + 'mi', + 'mn', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'rw', + 'sk', + 'sl', + 'sr', + 'st', + 'sw', + 'te', + 'tr', + 'uz', + 'vi', + 'yo', + 'zh_TW', + 'zu'], + 'related': ['center-and-variability'], + 'design_team': 'Amanda McGarry (lead designer); Catherine Carter; Kelly Findley ; Marilyn ' + 'Hartzell ; Ariel Paul; Kathy Perkins; Taliesin Smith; David Webb ; John ' + 'Blanco (developer) ; Sam Reid (developer) ; Marla Schulz (developer)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/mean-share-and-balance', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/mean-share-and-balance/latest/mean-share-and-balance-600.png'}, + { 'slug': 'membrane-transport', + 'is_phet_studio': True, + 'title': 'Membrane Transport', + 'description': 'Add different biological solutes to either side of a cell membrane and ' + 'observe their behavior based on solute properties and concentrations. Add ' + 'transport proteins and uncover some of the common mechanisms for ' + 'transporting solutes across a cell membrane.', + 'learning_goals': 'Predict when molecules and ions with specific size and charge ' + 'properties will move through the membrane and when they will ' + 'not.
Identify the conditions to trigger facilitated diffusion of ' + 'specific solutes by different types of transport proteins.
Explain ' + 'how energy is used in active transport to push molecules and ions ' + 'against their concentration gradient.', + 'subjects': ['biology', 'chemistry'], + 'topics': ['general-chemistry'], + 'detail_topics': ['Biology', 'Cell Membrane', 'Diffusion'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.0.6', + 'released': '2025-10-08', + 'updated': '2026-06-24', + 'is_new': True, + 'locales': [ 'ar', + 'ar_LY', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'fr', + 'ga', + 'hi', + 'ht', + 'hu', + 'is', + 'it', + 'iw', + 'ku', + 'lt', + 'nb', + 'nl', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'sr', + 'sv', + 'vi'], + 'related': ['neuron', 'diffusion'], + 'design_team': 'Brett Fiedler (designer); Taliesin Smith (designer); Jesse Greenberg ' + '(developer); Sam Reid (developer); Kathy Perkins; Holly Basta; Mike ' + 'Klymkowsky; Mark Nielsen; Amy Rouinfar; Nancy Salpepi; Emily Moore; Ashton ' + 'Morris (sound design)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/membrane-transport', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/membrane-transport/latest/membrane-transport-600.png'}, + { 'slug': 'models-of-the-hydrogen-atom', + 'is_phet_studio': True, + 'title': 'Models of the Hydrogen Atom', + 'description': 'This simulation is designed for undergraduate level students who are ' + 'studying atomic structure. The simulation could also be used by high ' + 'school students in advanced level physical science courses.', + 'learning_goals': 'Visualize different models of the hydrogen atom.
Relate the energy ' + 'absorbed and emitted by the electrons to the spectrometer ' + 'data.
Explain what experimental predictions each model ' + "makes.
Describe each historical model's differences and " + 'limitations.
Explain the relationship between the physical ' + 'picture of the orbits and the energy level diagram of an electron.', + 'subjects': ['physics', 'chemistry'], + 'topics': [ 'quantum-phenomena', + 'light-and-radiation', + 'general-chemistry', + 'quantum-chemistry'], + 'detail_topics': ['Quantum Mechanics', 'Atomic Structure', 'Bohr Model'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.0.9', + 'released': '2025-04-24', + 'updated': '2026-06-24', + 'is_new': True, + 'locales': [ 'ar', + 'be', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'fa', + 'fa_DA', + 'fr', + 'hi', + 'ht', + 'is', + 'it', + 'iw', + 'km', + 'kn', + 'ko', + 'ku', + 'lt', + 'mn', + 'ms', + 'mt', + 'nl', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'sr', + 'sw', + 'te', + 'th', + 'tr', + 'vi'], + 'related': ['rutherford-scattering'], + 'design_team': 'Diana López (lead designer); Amy Rouinfar (designer); Nancy Salpepi ' + '(designer); Kathy Perkins; Ariel Paul; Chris Malley (developer); Wendy ' + 'Adams; Michael Dubson; Sam McKagan ; Carl Wieman', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/models-of-the-hydrogen-atom', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/models-of-the-hydrogen-atom/latest/models-of-the-hydrogen-atom-600.png'}, + { 'slug': 'molarity', + 'is_phet_studio': False, + 'title': 'Molarity', + 'description': 'What determines the concentration of a solution? Learn about the ' + 'relationships between moles, liters, and molarity by adjusting the amount ' + 'of solute and solution volume. Change solutes to compare different ' + 'chemical compounds in water.', + 'learning_goals': 'Describe the relationships between volume and amount of solute to ' + 'concentration.
Explain how solution color and concentration are ' + 'related.
Calculate the concentration of solutions in units of ' + 'molarity (mol/L).
Use molarity to calculate the dilution of ' + 'solutions.
Compare solubility limits between solutes.', + 'subjects': ['chemistry'], + 'topics': ['general-chemistry'], + 'detail_topics': ['Volume', 'Moles', 'Molarity'], + 'grades': ['high', 'university'], + 'version': '1.5.24', + 'released': '2013-08-16', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_CO', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mk', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['beers-law-lab', 'concentration'], + 'design_team': 'Chris Malley (lead developer); John Blanco (developer); Michael Kauzmann ' + '(developer); Taylor Want (developer); Julia Chamberlain; Kelly Lancaster; ' + 'Emily B. Moore; Matthew Moore; Robert Parson; Kathy Perkins; Taliesin ' + 'Smith; Brianna Tomlinson; Ashton Morris (sound design)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/molarity', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/molarity/latest/molarity-600.png'}, + { 'slug': 'molecule-polarity', + 'is_phet_studio': True, + 'title': 'Molecule Polarity', + 'description': 'When is a molecule polar? Change the electronegativity of atoms in a ' + 'molecule to see how it affects polarity. See how the molecule behaves in ' + 'an electric field. Change the bond angle to see how shape affects ' + 'polarity.', + 'learning_goals': 'Predict bond polarity using electronegativity values
Indicate ' + 'polarity with a polar arrow or partial charges
Rank bonds in order ' + 'of polarity
Predict molecular polarity using bond polarity and ' + 'molecular shape', + 'subjects': ['biology', 'chemistry'], + 'topics': ['general-chemistry'], + 'detail_topics': ['Dipole', 'Bonds', 'Electronegativity'], + 'grades': ['high', 'university'], + 'version': '2.0.3', + 'released': '2017-07-26', + 'updated': '2026-09-01', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'km', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mk', + 'mn', + 'ms', + 'mt', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'molecules-and-light', + 'molecule-shapes', + 'molecule-shapes-basics', + 'build-a-molecule'], + 'design_team': 'Kelly Lancaster (lead); Chris Malley (developer); Jonathan Olson ' + '(developer); Agustín Vallejo (developer); Julia Chamberlain; Emily Moore; ' + 'Matthew Moore; Robert Parson; Kathy Perkins; Amy Rouinfar; Nancy Salpepi; ' + 'Taliesin Smith', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/molecule-polarity', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/molecule-polarity/latest/molecule-polarity-600.png'}, + { 'slug': 'molecule-shapes', + 'is_phet_studio': True, + 'title': 'Molecule Shapes', + 'description': 'Explore molecule shapes by building molecules in 3D! How does molecule ' + 'shape change with different numbers of bonds and electron pairs? Find out ' + 'by adding single, double or triple bonds and lone pairs to the central ' + 'atom. Then, compare the model to real molecules!', + 'learning_goals': 'Recognize that molecule geometry is due to repulsions between electron ' + 'groups.
Recognize the difference between electron and molecular ' + 'geometry.
Name molecule and electron geometries for molecules with ' + 'up to six electron groups surrounding a central atom.
Compare bond ' + 'angle predictions from the VSEPR-based model to real ' + 'molecules.
Describe how lone pairs affect bond angles in real ' + 'molecules.', + 'subjects': ['chemistry'], + 'topics': ['general-chemistry'], + 'detail_topics': ['Bonds', 'Molecules', 'VSEPR'], + 'grades': ['high', 'university'], + 'version': '1.6.19', + 'released': '2015-03-24', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mi', + 'mk', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'se', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'build-an-atom', + 'molecule-shapes-basics', + 'molecule-polarity', + 'build-a-molecule'], + 'design_team': 'Emily B. Moore (lead); Jonathan Olson (developer); Kelly Lancaster; Julia ' + 'Chamberlain; Kelly Lancaster; Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/molecule-shapes', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/molecule-shapes/latest/molecule-shapes-600.png'}, + { 'slug': 'molecule-shapes-basics', + 'is_phet_studio': True, + 'title': 'Molecule Shapes: Basics', + 'description': 'Explore molecule shapes by building molecules in 3D! Find out how a ' + "molecule's shape changes as you add atoms to a molecule.", + 'learning_goals': 'Recognize that molecule shape is due to repulsions between ' + 'atoms.
Recognize that bonds are not fixed in place, but can rotate ' + 'around in response to repulsions.', + 'subjects': ['chemistry'], + 'topics': ['general-chemistry'], + 'detail_topics': ['Bonds', 'Molecules', 'VSEPR'], + 'grades': ['middle', 'high'], + 'version': '1.6.19', + 'released': '2015-03-24', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'kk', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['build-an-atom', 'molecule-shapes', 'molecule-polarity', 'build-a-molecule'], + 'design_team': 'Emily B. Moore (lead); Jonathan Olson (developer); Julia Chamberlain; ' + 'Kelly Lancaster; Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/molecule-shapes-basics', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/molecule-shapes-basics/latest/molecule-shapes-basics-600.png'}, + { 'slug': 'molecules-and-light', + 'is_phet_studio': False, + 'title': 'Molecules and Light', + 'description': 'Turn light source on to explore. Observe what happens in the observation ' + 'window as you set up different combinations of light source and molecule. ' + 'Note this simulation is the first to support our pan and zoom feature, so ' + 'zoom in for a closer look, if you need to.', + 'learning_goals': 'Explore how light interacts with molecules in our ' + 'atmosphere
Identify that absorption of light depends on the ' + 'molecule and the type of light
Relate the energy of the light to ' + 'the resulting motion
Identify that energy increases from microwave ' + 'to ultraviolet
Predict the motion of a molecule based on the type ' + 'of light it absorbs
Identify how the structure of a molecule ' + 'affects how it interacts with light', + 'subjects': ['physics', 'chemistry', 'earth-science'], + 'topics': ['light-and-radiation', 'general-chemistry'], + 'detail_topics': ['Light', 'Photons', 'Absorption'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.5.33', + 'released': '2015-04-07', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mk', + 'mn', + 'ms', + 'mt', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['beers-law-lab', 'molecule-polarity', 'greenhouse-effect'], + 'design_team': 'Amy Rouinfar (lead); Jesse Greenberg (developer); John Blanco (developer); ' + 'Sam Reid (developer); Aaron Davis (developer); Kelly Lancaster; Yuen-ying ' + 'Carpenter; Brett Fiedler; Patricia Loeblein; Wanda Diaz Merced; Emily B. ' + 'Moore; Matthew Moore; Robert Parson; Ariel Paul; Kathy Perkins; Taliesin ' + 'Smith; Brianna Tomlinson; Syan Zhou; Alex Klinger (graphic arts); Ashton ' + 'Morris (sound design); Mike Winters (sond design)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/molecules-and-light', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/molecules-and-light/latest/molecules-and-light-600.png'}, + { 'slug': 'my-solar-system', + 'is_phet_studio': True, + 'title': 'My Solar System', + 'description': 'Build your own system of heavenly bodies and watch the gravitational ' + 'ballet. With this orbit simulator, you can set initial positions, ' + 'velocities, and masses of 2, 3, or 4 bodies, and then see them orbit each ' + 'other.', + 'learning_goals': 'Predict how the position, mass, velocity, and distance between ' + 'planetary bodies affect their motion and orbits.
Illustrate how the ' + 'gravitational force controls the motions of the planets
Explore the ' + 'different motions that a group of planetary bodies can have. ' + "
Describe the behavior of the planet's velocity in different " + 'moments of its orbit.', + 'subjects': ['physics', 'earth-science'], + 'topics': ['motion'], + 'detail_topics': ['Gravitational Force', 'Astronomy', 'Orbits'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.3.16', + 'released': '2023-05-15', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'be', + 'bg', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'km', + 'ko', + 'ku', + 'lg', + 'lt', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'sk', + 'sl', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'tg', + 'th', + 'tr', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_TW', + 'zu'], + 'related': [ 'gravity-force-lab', + 'gravity-and-orbits', + 'gravity-force-lab-basics', + 'keplers-laws'], + 'design_team': 'Diana López Tavares (lead designer); Michael Dubson (original designer and ' + 'developer); Agustín Vallejo (lead developer); Jonathan Olson (developer); ' + 'Emily B. Moore; Sola Olateju; Ariel Paul; Kathy Perkins; Amy Rouinfar', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/my-solar-system', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/my-solar-system/latest/my-solar-system-600.png'}, + { 'slug': 'natural-selection', + 'is_phet_studio': False, + 'title': 'Natural Selection', + 'description': 'Explore how organisms with different traits survive various selection ' + 'agents within the environment.', + 'learning_goals': 'Determine which mutations are favored by the selection agents of ' + 'predators and food variety and which mutations are neutral. ' + '
Describe which traits change the survivability of an organism in ' + 'different environments.
Experiment with environments which produce ' + 'a stable population of bunnies, a population that dies out, and a ' + 'population that takes over the world.
Track genes through multiple ' + 'generations.
Compare how dominant and recessive genes get passed ' + 'onto offspring.', + 'subjects': ['biology'], + 'topics': [], + 'detail_topics': ['Genetics', 'Mutation', 'Selection'], + 'grades': ['elementary', 'middle', 'high'], + 'version': '1.5.15', + 'released': '2020-09-02', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fo', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'pt', + 'pt_BR', + 'ro', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['gene-expression-essentials'], + 'design_team': 'Chris Malley (lead developer); Amanda McGarry (lead designer); Wendy ' + 'Adams; Megan Hoffman; Oliver Nix; Jonathan Olson; Ariel Paul; Kathy ' + 'Perkins; Noah Podolefsky; Megan Lai (graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/natural-selection', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/natural-selection/latest/natural-selection-600.png'}, + { 'slug': 'neuron', + 'is_phet_studio': False, + 'title': 'Neuron', + 'description': 'Stimulate a neuron and monitor what happens. Pause, rewind, and move ' + 'forward in time in order to observe the ions as they move across the ' + 'neuron membrane.', + 'learning_goals': 'Describe why ions can or cannot move across neuron ' + 'membranes.
Identify leakage and gated channels, and describe the ' + 'function of each.
Describe how membrane permeability changes in ' + 'terms of different types of channels in a neuron.
Describe the ' + 'sequence of events that generates an action potential.', + 'subjects': ['biology'], + 'topics': [], + 'detail_topics': ['Biology', 'Cells', 'Neurons'], + 'grades': ['high', 'university'], + 'version': '1.1.36', + 'released': '2016-03-01', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'es_UY', + 'et', + 'eu', + 'fa', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mi', + 'mk', + 'mn', + 'ms', + 'mt', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['membrane-transport'], + 'design_team': 'Amanda McGarry; Noah Podolefsky ; Wendy Adams; Katherine Perkins; John ' + 'Blanco (developer); Sharfudeen Ashraf (developer)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/neuron', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/neuron/latest/neuron-600.png'}, + { 'slug': 'number-compare', + 'is_phet_studio': False, + 'title': 'Number Compare', + 'description': 'Compare numbers represented as numerals, objects, groups, and ten frames. ' + 'Use a sentence, number line, or stacked units to determine the larger ' + 'group. Hear the comparison spoken aloud in two different languages.', + 'learning_goals': 'Coordinate across multiple representations of a number: written/spoken ' + 'word, numeral, ten-frame, ones, and objects.
Identify the number of ' + 'objects in a group.
Compare numbers and quantities up to 10, using ' + 'a strategy such as grouping, subitizing, counting, or a number line. ' + '
Compare quantities in ten frames.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Numbers', 'Quantities', 'Cardinality'], + 'grades': ['elementary'], + 'version': '1.0.13', + 'released': '2023-04-16', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lt', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'pr', + 'ps', + 'pt_BR', + 'ro', + 'rw', + 'sk', + 'sl', + 'sn', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'tk', + 'tr', + 'tw', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['make-a-ten', 'number-play', 'number-pairs'], + 'design_team': 'Amanda McGarry (lead designer); Chris Klusendorf (lead developer); Luisa ' + 'Vargas (developer); Sylvia Celedon-Pattichis; Ariel Paul; Kathy Perkins; ' + 'Ian Whitacre; Mariah Hermsmeyer (graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/number-compare', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/number-compare/latest/number-compare-600.png'}, + { 'slug': 'number-line-distance', + 'is_phet_studio': False, + 'title': 'Number Line: Distance', + 'description': 'Subtraction is more than an operation- it is also the distance between two ' + 'numbers! Explore the distance between a person and house, a bird and a ' + 'fish, and the difference in temperature between two locations. Compare ' + 'absolute distance and directed distance, and strengthen your number line ' + 'muscles.', + 'learning_goals': 'Calculate the difference between two integers using ' + 'subtraction
Represent subtraction of integers as the distance ' + 'between integers
Reason about subtraction of integers in terms of ' + 'number-locations and directed distances
Show that the distance ' + 'between two integers on the number line is the absolute value of their ' + 'difference
Recognize and generate equivalence classes of integer ' + 'differences (e.g., 5 – 2 = 6 – 3)', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Number Line', 'Subtraction', 'Integers'], + 'grades': ['middle'], + 'version': '1.1.11', + 'released': '2021-08-13', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lt', + 'mi', + 'mn', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'pt_BR', + 'qu', + 'ro', + 'rw', + 'sk', + 'sl', + 'sn', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'tk', + 'tr', + 'tw', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['graphing-lines', 'number-line-integers', 'number-line-operations'], + 'design_team': 'Amanda McGarry (lead design); Saurabh Totey (lead developer); John Blanco ' + '(developer); Kathy Perkins; Ian Whitacre', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/number-line-distance', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/number-line-distance/latest/number-line-distance-600.png'}, + { 'slug': 'number-line-integers', + 'is_phet_studio': False, + 'title': 'Number Line: Integers', + 'description': 'What do elevation, account balances, and temperature have in common? They ' + 'can all be represented on a number line. Compare integers on a number line ' + 'and in an inequality statement. Discover the meaning of absolute value.', + 'learning_goals': 'Use positive and negative numbers to represent quantities in multiple ' + 'contexts.
Explain the meaning of positive values, negative values, ' + 'and zero, in multiple contexts.
Describe the location of a point on ' + 'a number line with respect to another number.
Describe the location ' + 'of a point on a number line with respect to its opposite
Define ' + 'the absolute value of a number as its distance from zero. ' + '
Interpret inequality statements as statements about the relative ' + 'position of two integers on a number line diagram.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Number Line', 'Integers', 'Absolute Value'], + 'grades': ['elementary', 'middle'], + 'version': '1.2.12', + 'released': '2020-01-29', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mi', + 'mn', + 'ms', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': [ 'unit-rates', + 'proportion-playground', + 'expression-exchange', + 'equality-explorer', + 'number-line-operations', + 'ratio-and-proportion'], + 'design_team': 'Amanda McGarry (lead design); John Blanco (lead developer); Chris ' + 'Klusendorf (developer); Saurabh Totey (developer); Ariel Paul; Kathy ' + 'Perkins; David Webb; Ian Whitacre', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/number-line-integers', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/number-line-integers/latest/number-line-integers-600.png'}, + { 'slug': 'number-line-operations', + 'is_phet_studio': False, + 'title': 'Number Line: Operations', + 'description': 'Explore operations with integers using an authentic context of net worth, ' + 'then generalize with a number line. Experiment with adding and subtracting ' + 'both positives and negatives, and make predictions about whether the total ' + 'will be positive or negative.', + 'learning_goals': 'Represent addition and subtraction of integers on a horizontal number ' + 'line
Reason about addition and subtraction of integers in terms of ' + 'number-locations
Use logical necessity to reason that addition has ' + 'the opposite effect as subtraction or that adding (or subtracting) a ' + 'negative integer has the opposite effect as adding (or subtracting) a ' + 'positive integer.
Recognize and generate equivalence classes of ' + 'integer sums and differences
Show that a number and its additive ' + 'inverse (opposite) have a sum of 0
Using a net worth context, ' + 'describe situations that have a positive sum or difference, negative ' + 'sum or difference, and zero sum or difference
View adding a ' + 'negative as equivalent to subtracting a positive, and explain why this ' + 'relationship makes sense in contexts such as net worth.
View ' + 'subtracting a negative as equivalent to adding a positive, and explain ' + 'why this relationship makes sense in contexts such as net ' + 'worth.
Apply a number line model for addition and subtraction to ' + 'new contexts.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Number Line', 'Integers', 'Operations'], + 'grades': ['middle'], + 'version': '1.1.10', + 'released': '2021-02-04', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mi', + 'mn', + 'ms', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tk', + 'tr', + 'tw', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': [ 'expression-exchange', + 'equality-explorer', + 'number-line-integers', + 'number-line-distance'], + 'design_team': 'Amanda McGarry (lead design); John Blanco (lead developer); Ariel Paul; ' + 'Kathy Perkins; Ian Whitacre', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/number-line-operations', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/number-line-operations/latest/number-line-operations-600.png'}, + { 'slug': 'number-pairs', + 'is_phet_studio': True, + 'title': 'Number Pairs', + 'description': 'Create number pairs to compose, decompose, and add numbers from 0-20 in ' + 'multiple contexts. Play with magic beads and kittens that can change ' + 'color! Add numbers on a number line!', + 'learning_goals': 'Decompose numbers less than or equal to 10/20 into pairs in more than ' + 'one way.
Interpret a number pair representation and explain the ' + 'representation.
Describe the decomposition of a number based on ' + 'either the attribute or the location of objects.
Describe a number ' + 'less than or equal to 10/20 as a sum (or decomposition) of two ' + 'numbers.
Determine the unknown whole number in an implied or ' + 'explicit addition or subtraction problem relating three whole numbers.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Composition', 'Number Line', 'Decomposition'], + 'grades': ['elementary'], + 'version': '2.0.4', + 'released': '2025-05-09', + 'updated': '2026-06-24', + 'is_new': True, + 'locales': [ 'ar', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'fa', + 'fr', + 'gu', + 'hi', + 'ht', + 'is', + 'it', + 'iw', + 'ko', + 'ku', + 'mn', + 'nl', + 'pl', + 'pt_BR', + 'ro', + 'sr', + 'te', + 'tr', + 'vi'], + 'related': ['make-a-ten', 'number-play', 'number-compare'], + 'design_team': 'Catherine Carter (lead designer); Dom Carorora; Sylvia Celedón-Pattichis; ' + 'Amanda McGarry; Ariel Paul; Kathy Perkins; Taliesin Smith; Ian Whitacre; ' + 'Chris Malley; Marla Schulz (lead developer); Sam Reid', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/number-pairs', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/number-pairs/latest/number-pairs-600.png'}, + { 'slug': 'number-play', + 'is_phet_studio': False, + 'title': 'Number Play', + 'description': 'Play with counting and cardinality. Compare representations, including ' + 'word, numeral, ten frame, and objects. Hear the number spoken aloud in two ' + 'different languages. Challenge yourself to subitize numbers up to ten!', + 'learning_goals': 'Coordinate across multiple representations of a number: written/spoken ' + 'word, numeral, ten-frame, ones, and objects.
Subitize to 5. ' + '
Subitize to 10.
Predict the next number in a ' + 'sequence.
Identify the number of objects in a group.
Given a ' + 'number from 1-20, count out that many objects.
Determine how many ' + 'objects there are in a group or ten-frame, up to 20.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Numbers', 'Quantities', 'Cardinality'], + 'grades': ['elementary'], + 'version': '1.1.13', + 'released': '2023-04-14', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'mn', + 'ms', + 'nl', + 'ny', + 'om', + 'pl', + 'pr', + 'ps', + 'pt_BR', + 'ro', + 'rw', + 'sk', + 'sl', + 'sn', + 'sq', + 'sr', + 'st', + 'sw', + 'te', + 'tk', + 'tr', + 'tw', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['make-a-ten', 'number-compare', 'number-pairs'], + 'design_team': 'Amanda McGarry (lead designer); Chris Klusendorf (developer); Luisa Vargas ' + '(developer) ; Sylvia Celedón-Pattichis; Ariel Paul; Kathy Perkins ; Ian ' + 'Whitacre', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/number-play', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/number-play/latest/number-play-600.png'}, + { 'slug': 'ohms-law', + 'is_phet_studio': False, + 'title': "Ohm's Law", + 'description': "See how the equation form of Ohm's law relates to a simple circuit. Adjust " + "the voltage and resistance, and see the current change according to Ohm's " + 'law.', + 'learning_goals': 'Predict how current will change when resistance of the circuit is fixed ' + 'and voltage is varied.
Predict how current will change when ' + 'voltage of the circuit is fixed and resistance is varied.', + 'subjects': ['physics', 'math'], + 'topics': ['electricity-magnets-and-circuits', 'math-applications'], + 'detail_topics': ['Circuits', 'Current', 'Voltage'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.4.31', + 'released': '2013-08-16', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mg', + 'mi', + 'mk', + 'ml', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'ne', + 'nl', + 'nn', + 'ny', + 'om', + 'or', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'resistance-in-a-wire', + 'circuit-construction-kit-dc', + 'circuit-construction-kit-dc-virtual-lab', + 'circuit-construction-kit-ac'], + 'design_team': 'Michael Dubson (lead, developer); John Blanco (developer); Michael ' + 'Kauzmann (developer); Martin Veillette (developer); Emily B. Moore; Ariel ' + 'Paul; Kathy Perkins; Taliesin Smith; Brianna Tomlinson; Mindy Gratny ' + '(interviewer); Ashton Morris (Sound Design)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/ohms-law', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/ohms-law/latest/ohms-law-600.png'}, + { 'slug': 'pendulum-lab', + 'is_phet_studio': False, + 'title': 'Pendulum Lab', + 'description': 'Play with one or two pendulums and discover how the period of a simple ' + 'pendulum depends on the length of the string, the mass of the pendulum ' + 'bob, the strength of gravity, and the amplitude of the swing. Observe the ' + 'energy in the system in real-time, and vary the amount of friction. ' + 'Measure the period using the stopwatch or period timer. Use the pendulum ' + 'to find the value of g on Planet X. Notice the anharmonic behavior at ' + 'large amplitude.', + 'learning_goals': 'Design experiments to determine which variables affect the period of a ' + 'pendulum
Quantitatively describe how the period of a pendulum ' + 'depends on these variables
Explain the small-angle approximation, ' + 'and define what constitutes a “small” angle
Determine the ' + 'gravitational acceleration of Planet X
Explain the conservation of ' + 'mechanical energy, using kinetic energy and gravitational potential ' + 'energy
Describe the Energy Graph from the position and speed of the ' + 'pendulum', + 'subjects': ['physics', 'math'], + 'topics': ['motion', 'work-energy-and-power', 'math-applications'], + 'detail_topics': ['Conservation of Energy', 'Period', 'Pendulum'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.0.35', + 'released': '2017-09-13', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['masses-and-springs', 'masses-and-springs-basics', 'energy-skate-park'], + 'design_team': 'Jonathon Olson (developer); Amy Rouinfar (lead designer); Michael Dubson ' + '(original designer); Trish Loeblein; Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/pendulum-lab', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/pendulum-lab/latest/pendulum-lab-600.png'}, + { 'slug': 'plinko-probability', + 'is_phet_studio': False, + 'title': 'Plinko Probability', + 'description': 'Drop balls through a triangular grid of pegs and see them accumulate in ' + 'containers. Switch to a histogram view and compare the distribution of ' + 'balls to an ideal binomial distribution. Adjust the binary probability and ' + 'develop your knowledge of statistics!', + 'learning_goals': 'Predict the bin where a single ball might fall
Repeat trials of ' + '100 balls and compare the outcomes
Count the number of balls in a ' + 'bin and relate that to the probability of falling in that ' + 'bin
Compare and interpret empirical and theoretical ' + 'statistics
Apply the plinko simulation as a model to other ' + 'scenarios where there are weighted statistics', + 'subjects': ['physics', 'math'], + 'topics': ['heat-and-thermo', 'math-concepts'], + 'detail_topics': ['Statistics', 'Probability', 'Histograms'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.1.36', + 'released': '2016-09-30', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lo', + 'lv', + 'mg', + 'mn', + 'mr', + 'ms', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': [ 'least-squares-regression', + 'projectile-data-lab', + 'projectile-sampling-distributions', + 'quantum-measurement', + 'quantum-coin-toss'], + 'design_team': 'Martin Veillette (developer); Denzell Barnett (developer); Chris Malley ' + '(developer) ; Guillermo Ramos-Macias (developer) ; Amanda McGarry (lead ' + 'designer); Michael Dubson (original designer); Karina K. Hensberry; Trish ' + 'Loeblein; Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/plinko-probability', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/plinko-probability/latest/plinko-probability-600.png'}, + { 'slug': 'projectile-data-lab', + 'is_phet_studio': True, + 'title': 'Projectile Data Lab', + 'description': 'The Projectile Data Lab simulation allows students to engage in hands-on ' + 'data collection and analysis through the context of projectile motion.', + 'learning_goals': 'Visualize and hear data resulting from real-world randomness in launch ' + 'speed and angle
Attribute data variability to physical properties ' + 'of the apparatus
Design experiments to investigate the effect of ' + 'different launch parameters
Compare sampling distributions and ' + 'explore the effect of sample size', + 'subjects': ['physics', 'math'], + 'topics': ['motion', 'math-applications'], + 'detail_topics': ['Projectile Motion', 'Measurement', 'Data'], + 'grades': ['high', 'university'], + 'version': '1.0.12', + 'released': '2024-05-03', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'ar', + 'ar_LY', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'fa', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'is', + 'it', + 'iw', + 'ko', + 'ku', + 'mi', + 'mn', + 'ms', + 'nl', + 'pl', + 'pt', + 'pt_BR', + 'ro', + 'sr', + 'sv', + 'sw', + 'te', + 'th', + 'tr', + 'vi', + 'yo', + 'zh_TW'], + 'related': ['plinko-probability', 'projectile-motion', 'projectile-sampling-distributions'], + 'design_team': 'Matthew Blackman (lead designer); Cathy Carter (designer); Sam Reid ' + '(developer); Ariel Paul; Kathy Perkins; Amy Rouinfar; Kelly Findley; Emily ' + 'B. Moore; Ashton Morris (sound design)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/projectile-data-lab', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/projectile-data-lab/latest/projectile-data-lab-600.png'}, + { 'slug': 'projectile-motion', + 'is_phet_studio': False, + 'title': 'Projectile Motion', + 'description': 'Blast a car out of a cannon, and challenge yourself to hit a target! Learn ' + 'about projectile motion by firing various objects. Set parameters such as ' + 'angle, initial speed, and mass. Explore vector representations, and add ' + 'air resistance to investigate the factors that influence drag.', + 'learning_goals': 'Determine how each parameter (initial height, initial angle, initial ' + 'speed, mass, diameter, and altitude) affects the trajectory of an ' + 'object, with and without air resistance.
Predict how varying the ' + 'initial conditions will affect a projectile’s path, and provide an ' + 'explanation for the prediction.
Estimate where an object will land, ' + 'given its initial conditions.
Determine that the x and y motion of ' + 'a projectile are independent.
Investigate the variables that ' + 'affect the drag force.
Describe the the effect that the drag force ' + 'has on the velocity and acceleration.
Discuss projectile motion ' + 'using common vocabulary (such as: launch angle, initial speed, initial ' + 'height, range, time).', + 'subjects': ['physics', 'math'], + 'topics': ['motion', 'math-applications'], + 'detail_topics': ['Kinematics', 'Vectors', 'Projectile Motion'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '1.0.34', + 'released': '2017-08-24', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['projectile-data-lab', 'projectile-sampling-distributions'], + 'design_team': 'Amy Rouinfar (lead designer); Andrea Lin (developer); Wendy Adams; John ' + 'Blanco; Mike Dubson; Amanda McGarry; Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/projectile-motion', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/projectile-motion/latest/projectile-motion-600.png'}, + { 'slug': 'projectile-sampling-distributions', + 'is_phet_studio': True, + 'title': 'Projectile Sampling Distributions', + 'description': 'Engage in hands-on data collection and analysis through the rich context ' + 'of launching projectiles. Explore how different launch parameters create a ' + 'variety of distributions as you watch the Central Limit Theorem come to ' + 'life!', + 'learning_goals': 'Visualize and hear how a sampling distribution is created in real ' + 'time
Develop an intuitive sense of the Central Limit ' + 'Theorem
Compare sampling distributions from a variety of ' + 'interesting populations
Explore ways to make inferences more ' + 'accurately', + 'subjects': ['math'], + 'topics': ['math-applications'], + 'detail_topics': ['Projectile Motion', 'Data', 'Variability'], + 'grades': ['high', 'university'], + 'version': '1.0.13', + 'released': '2024-05-03', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'ar', + 'ar_LY', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'fa', + 'fr', + 'ha', + 'hi', + 'hr', + 'ht', + 'is', + 'it', + 'iw', + 'ko', + 'ku', + 'mn', + 'ms', + 'nl', + 'pl', + 'pt', + 'pt_BR', + 'ro', + 'sr', + 'sw', + 'te', + 'th', + 'vi', + 'yo', + 'zh_TW'], + 'related': ['plinko-probability', 'projectile-motion', 'projectile-data-lab'], + 'design_team': 'Matthew Blackman (lead designer); Cathy Carter (designer); Sam Reid ' + '(developer); Ariel Paul; Kathy Perkins; Amy Rouinfar; Kelly Findley; Emily ' + 'B. Moore; Ashton Morris (sound design)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/projectile-sampling-distributions', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/projectile-sampling-distributions/latest/projectile-sampling-distributions-600.png'}, + { 'slug': 'proportion-playground', + 'is_phet_studio': False, + 'title': 'Proportion Playground', + 'description': 'Play with ratios and proportions by designing a necklace, throwing paint ' + 'balloons, playing billiards, or shopping for apples! Make predictions ' + 'about proportions before they are revealed.', + 'learning_goals': 'Use a ratio as a descriptive characteristic
Compare the meaning of ' + 'a ratio in different contexts
Use scaling to build proportions or ' + 'find a missing value in a proportion
Simplify ratios to build ' + 'proportions or find a missing value in a proportion
Use ' + 'multiplicative reasoning to solve problems', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Ratios', 'Proportional Reasoning', 'Unit Rate'], + 'grades': ['elementary', 'middle'], + 'version': '1.0.33', + 'released': '2017-05-31', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lo', + 'lv', + 'mg', + 'mi', + 'mn', + 'ms', + 'my', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': [ 'unit-rates', + 'equality-explorer-basics', + 'number-line-integers', + 'ratio-and-proportion', + 'quadrilateral'], + 'design_team': 'Amanda McGarry (lead designer); Andrea Lin (development); Sam Reid ' + '(development); Jonathan Olson (development); Karina K.R. Hensberry; Ariel ' + 'Paul; Beth Stade; Ian Whitacre', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/proportion-playground', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/proportion-playground/latest/proportion-playground-600.png'}, + { 'slug': 'quadrilateral', + 'is_phet_studio': False, + 'title': 'Quadrilateral', + 'description': 'Explore the properties of and relationships between named four-sided ' + 'shapes by moving sides and corners of a quadrilateral.', + 'learning_goals': 'Predict how moving a side or corner will change or maintain the current ' + 'named shape.
Determine the similarities and differences between ' + 'quadrilaterals by looking at their side, angle, and diagonal measures ' + 'and understand that the shared attributes can define a larger ' + 'category.
Classify quadrilaterals based on the presence or absence ' + 'of parallel or perpendicular lines, or the presence or absence of equal ' + 'or right angles.
Classify quadrilaterals in a hierarchy based on ' + 'shared geometric properties.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Geometry'], + 'grades': ['elementary', 'middle'], + 'version': '1.1.14', + 'released': '2023-05-25', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_LY', + 'az', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fr', + 'gu', + 'ha', + 'hi', + 'ht', + 'hu', + 'is', + 'it', + 'iw', + 'ki', + 'kk', + 'ko', + 'ku', + 'mn', + 'ms', + 'nl', + 'pl', + 'pt_BR', + 'ro', + 'sk', + 'sl', + 'sr', + 'st', + 'sw', + 'te', + 'tr', + 'uz', + 'vi', + 'yo', + 'zh_TW', + 'zu'], + 'related': [ 'area-builder', + 'proportion-playground', + 'equality-explorer', + 'ratio-and-proportion'], + 'design_team': 'Jesse Greenberg (developer); Brett Fiedler; Emily B. Moore; Taliesin ' + 'Smith; Ashton Morris (sound design); Sofia Tancredi, Dor Abrahamson ' + '(Embodied Design Research Lab, UC Berkeley); Scott Lambert, Jenna ' + 'Gorlewicz (CHROME Lab, St. Louis University)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/quadrilateral', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/quadrilateral/latest/quadrilateral-600.png'}, + { 'slug': 'quantum-coin-toss', + 'is_phet_studio': True, + 'title': 'Quantum Coin Toss', + 'description': 'Compare the similarities and differences between classical biased coins ' + 'and quantum "coins" that can be prepared in a superposition of states!', + 'learning_goals': 'Develop an interpretation of the superposition of states of a 2-level ' + 'quantum system.
Describe what happens when a superposition of ' + 'states is observed.
Explain how measurements of a classical biased ' + 'coin compare and contrast to those of a quantum system.', + 'subjects': ['physics', 'chemistry'], + 'topics': ['quantum-chemistry'], + 'detail_topics': ['Quantum Mechanics', 'Probability', 'Quantum Measurement'], + 'grades': ['high', 'university'], + 'version': '1.0.4', + 'released': '2025-05-21', + 'updated': '2026-06-24', + 'is_new': True, + 'locales': [ 'ar', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'fa', + 'fa_DA', + 'fr', + 'gu', + 'hi', + 'ht', + 'is', + 'it', + 'iw', + 'ku', + 'mn', + 'nl', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'sr', + 'te', + 'vi'], + 'related': ['plinko-probability', 'quantum-measurement'], + 'design_team': 'Ariel Paul (lead designer); Amy Rouinfar (lead designer); Mike Bennet ; ' + 'John Blanco (developer); Josh Combes; Aiko Kyle; Phil Makotyn; Kathy ' + 'Perkins; Ana Maria Rey; Agustín Vallejo (developer); Martin Veillette', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/quantum-coin-toss', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/quantum-coin-toss/latest/quantum-coin-toss-600.png'}, + { 'slug': 'quantum-measurement', + 'is_phet_studio': True, + 'title': 'Quantum Measurement', + 'description': 'Explore 2-level quantum systems and the results of measuring quantum ' + 'states. Discover the similarities and differences between how a flip of a ' + 'classical biased coin compares to observations of a quantum "coin". ' + 'Discover how a quantum state can be visualized with the Bloch sphere ' + 'representation.', + 'learning_goals': 'Predict the distribution of results from repeated measurements of a ' + '2-level quantum system.
Compare the differences in behavior when ' + 'measuring classical and quantum systems.
Describe how the state of ' + 'a 2-level system can be mapped to the Bloch Sphere ' + 'representation.
Predict the possible observations when spin-1/2 ' + 'particles are directed through multiple Stern-Gerlach experiments.', + 'subjects': ['physics', 'chemistry'], + 'topics': ['quantum-phenomena', 'quantum-chemistry'], + 'detail_topics': ['Quantum Mechanics', 'Quantum Measurement', 'Spin 1/2 Particles'], + 'grades': ['high', 'university'], + 'version': '1.0.5', + 'released': '2025-05-08', + 'updated': '2026-06-24', + 'is_new': True, + 'locales': [ 'ar', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'fa', + 'fa_DA', + 'fr', + 'gu', + 'hi', + 'ht', + 'is', + 'it', + 'iw', + 'ko', + 'ku', + 'mn', + 'nl', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'sr', + 'te', + 'vi'], + 'related': [ 'plinko-probability', + 'vector-addition', + 'quantum-coin-toss', + 'quantum-wave-interference'], + 'design_team': 'Ariel Paul (lead designer); Amy Rouinfar (lead designer); Mike Bennet ; ' + 'John Blanco (developer); Josh Combes; Aiko Kyle; Phil Makotyn; Kathy ' + 'Perkins; Ana Maria Rey; Agustín Vallejo (developer); Martin Veillette', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/quantum-measurement', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/quantum-measurement/latest/quantum-measurement-600.png'}, + { 'slug': 'quantum-wave-interference', + 'is_phet_studio': True, + 'title': 'Quantum Wave Interference', + 'description': 'When do photons, electrons, and atoms behave like particles and when do ' + 'they behave like waves? Watch waves spread out and interfere as they pass ' + 'through a double slit, then get detected on a screen as tiny dots. Use ' + 'quantum detectors to explore how measurements change the waves and the ' + 'patterns they produce on the screen.', + 'learning_goals': 'Recognize that in a double-slit experiment, particles arrive as ' + 'discrete, localized detections, and that the interference pattern ' + 'becomes visible only as many such events accumulate over ' + 'time.
Interpret an interference pattern formed from many individual ' + 'detections as evidence that quantum objects exhibit both localized ' + 'detection events and wave-like interference behavior.
Relate the ' + 'amplitude of a wavefunction to the probability of detecting a particle ' + 'at different positions, and interpret probability patterns on the ' + 'screen.
Explain how measurements (interactions) that reveal which ' + 'path a particle takes change the observed pattern and eliminate ' + 'interference.
Predict qualitatively and quantitatively how changing ' + 'wavelength, particle properties, or slit geometry affects the observed ' + 'interference pattern.
Relate particle momentum to wavelength using ' + 'the de Broglie relationship.
Recognize large range of size scales ' + 'involved in quantum interference experiments.', + 'subjects': ['physics', 'chemistry'], + 'topics': ['sound-and-waves', 'quantum-phenomena', 'quantum-chemistry'], + 'detail_topics': ['Wave Function', 'Interference', 'Wave-Particle Duality'], + 'grades': ['high', 'university'], + 'version': '1.0.0', + 'released': '2026-09-10', + 'updated': '2026-09-10', + 'is_new': True, + 'locales': ['da', 'en', 'pl'], + 'related': ['wave-interference', 'quantum-measurement'], + 'design_team': 'Matthew Blackman (lead designer); Sam Reid (lead developer); Taliesin ' + 'Smith; Amy Rouinfar; Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/quantum-wave-interference', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/quantum-wave-interference/latest/quantum-wave-interference-600.png'}, + { 'slug': 'ratio-and-proportion', + 'is_phet_studio': False, + 'title': 'Ratio and Proportion', + 'description': 'Play with the left and right hands in different ways, and explore ratio ' + 'and proportion. Start on the Discover screen to find each challenge ratio ' + 'by moving the hands. Then, on the Create screen, set your own challenge ' + "ratios. Once you've found a challenge ratio, try to move both hands while " + 'maintaining the challenge ratio through proportional reasoning.', + 'learning_goals': 'Understand the concept of a ratio and use ratio language to describe a ' + 'ratio relationship between two hand heights.
Understand the concept ' + 'of a unit rate a/b associated with a ratio a:b, and use rate language ' + 'in the context of a ratio relationship.
Recognize and represent ' + 'proportional relationships between hand heights.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Ratios', 'Proportional Reasoning'], + 'grades': ['elementary', 'middle'], + 'version': '1.2.22', + 'released': '2021-04-05', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mi', + 'mn', + 'ms', + 'my', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': [ 'unit-rates', + 'proportion-playground', + 'equality-explorer-basics', + 'number-line-integers', + 'quadrilateral'], + 'design_team': 'Michael Kauzmann (developer); John Blanco (developer); Brett Fiedler ; ' + 'Emily B. Moore; Matthew Moore; Taliesin Smith; Amanda McGarry; Ashton ' + 'Morris (sound design); Dor Abrahamson and the Embodied Design Research Lab ' + '(UC Berkeley); Clement Zheng, Peter Gyory, and Ellen Do from the ACME Lab ' + '(CU Boulder ATLAS Institute)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/ratio-and-proportion', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/ratio-and-proportion/latest/ratio-and-proportion-600.png'}, + { 'slug': 'reactants-products-and-leftovers', + 'is_phet_studio': False, + 'title': 'Reactants, Products and Leftovers', + 'description': 'Create your own sandwich and then see how many sandwiches you can make ' + 'with different amounts of ingredients. Do the same with chemical ' + 'reactions. See how many products you can make with different amounts of ' + 'reactants. Play a game to test your understanding of reactants, products ' + 'and leftovers. Can you get a perfect score on each level?', + 'learning_goals': 'Recognize that atoms are conserved during a chemical reaction.
Use ' + 'concrete everyday experiences (such as making sandwiches) to describe ' + 'the what a limiting reactant means in chemical reactions.
Identify ' + 'the limiting reactant in a chemical reaction.
Predict the products ' + 'and leftovers after reaction, based on the quantities of reactants and ' + 'ratios of molecules in the balanced chemical equation.
Predict the ' + 'initial amounts of reactants given the amount of products and leftovers ' + 'using the concept of limiting reactant.
Translate from symbolic ' + '(chemical formula) to molecular (pictorial) representations of matter.', + 'subjects': ['chemistry'], + 'topics': ['general-chemistry'], + 'detail_topics': ['Chemical Reactions', 'Limiting Reactants'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.3.5', + 'released': '2015-01-27', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mg', + 'mi', + 'mk', + 'mn', + 'mr', + 'ms', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['balancing-chemical-equations'], + 'design_team': 'Yuen-ying Carpenter (lead); Kelly Lancaster (lead); Chris Malley ' + '(developer); Wendy Adams; Julia Chamberlain; Patricia Loeblein; Emily B. ' + 'Moore; Robert Parson; Ariel Paul; Kathy Perkins; Amy Rouinfar', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/reactants-products-and-leftovers', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/reactants-products-and-leftovers/latest/reactants-products-and-leftovers-600.png'}, + { 'slug': 'resistance-in-a-wire', + 'is_phet_studio': True, + 'title': 'Resistance in a Wire', + 'description': 'Observe changes to the equation and wire as you play with the resistivity, ' + 'length, and area sliders.', + 'learning_goals': 'Predict how changing each variable will affect the resistance. ' + '
Explain the difference between resistance and resistivity.', + 'subjects': ['physics', 'math'], + 'topics': ['electricity-magnets-and-circuits', 'math-applications'], + 'detail_topics': ['Circuits', 'Resistance', 'Resistivity'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.7.0', + 'released': '2013-08-16', + 'updated': '2026-06-26', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mg', + 'mi', + 'mk', + 'ml', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'ne', + 'nl', + 'ny', + 'om', + 'or', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'ohms-law', + 'circuit-construction-kit-dc', + 'circuit-construction-kit-dc-virtual-lab', + 'circuit-construction-kit-ac'], + 'design_team': 'Michael Dubson (lead, developer); John Blanco (developer); Jesse Greenberg ' + '(developer); Michael Kauzmann (developer); Emily B. Moore; Ariel Paul; ' + 'Kathy Perkins; Amy Rouinfar; Taliesin Smith; Brianna Tomlinson; Wendy ' + 'Adams (interviewer); Mindy Gratny (interviewer); Ashton Morris (sound ' + 'design); Mike Winters (sound design)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/resistance-in-a-wire', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/resistance-in-a-wire/latest/resistance-in-a-wire-600.png'}, + { 'slug': 'rutherford-scattering', + 'is_phet_studio': False, + 'title': 'Rutherford Scattering', + 'description': 'How did Rutherford figure out the structure of the atom without being able ' + 'to see it? Simulate the famous experiment in which he disproved the Plum ' + 'Pudding model of the atom by observing alpha particles bouncing off atoms ' + 'and determining that they must have a small core.', + 'learning_goals': 'Describe the qualitative difference between scattering off positively ' + 'charged nucleus and electrically neutral plum pudding atom.
For ' + 'charged nucleus, describe qualitatively how angle of deflection depends ' + 'on: Energy of incoming particle, Impact parameter, Charge of target', + 'subjects': ['physics', 'chemistry'], + 'topics': ['quantum-phenomena', 'general-chemistry'], + 'detail_topics': ['Quantum Mechanics', 'Atomic Structure', 'Atomic Nuclei'], + 'grades': ['high', 'university'], + 'version': '1.2.0', + 'released': '2016-08-17', + 'updated': '2026-07-14', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mk', + 'mn', + 'mr', + 'ms', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sn', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['models-of-the-hydrogen-atom'], + 'design_team': 'Amy Hanson (lead designer); Sam McKagan (lead designer); Jesse Greenberg ' + '(lead developer); Chris Malley (developer); Dave Schmitz (developer); ' + 'Agustín Vallejo (developer); Wendy Adams; Michael Dubson; Noah ' + 'Finkelstein; Danielle Harlow; Emily B. Moore; Ariel Paul; Kathy Perkins; ' + 'Noah Podolefsky; Amy Rouinfar; Marla Schulz; Taliesin Smith; Carl Wieman', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/rutherford-scattering', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/rutherford-scattering/latest/rutherford-scattering-600.png'}, + { 'slug': 'states-of-matter', + 'is_phet_studio': False, + 'title': 'States of Matter', + 'description': 'Watch different types of molecules form a solid, liquid, or gas. Add or ' + 'remove heat and watch the phase change. Change the temperature or volume ' + 'of a container and see a pressure-temperature diagram respond in real ' + 'time. Relate the interaction potential to the forces between molecules.', + 'learning_goals': 'Describe a molecular model for solids, liquids, and gases.
Extend ' + 'this model to phase changes.
Describe how heating or cooling ' + 'changes the behavior of the molecules.
Describe how changing the ' + 'volume can affect temperature, pressure, and state.
Relate a ' + 'pressure-temperature diagram to the behavior of ' + 'molecules.
Interpret graphs of interatomic potential.
Describe ' + 'how forces on atoms relate to the interaction potential.
Describe ' + 'the physical meaning of the parameters in the Lennard-Jones potential, ' + 'and how this relates to the molecule behavior.', + 'subjects': ['physics', 'chemistry'], + 'topics': ['heat-and-thermo', 'general-chemistry'], + 'detail_topics': ['Dipole', 'Interaction Potential', 'Atomic Bonding'], + 'grades': ['high', 'university'], + 'version': '1.2.23', + 'released': '2016-12-21', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'or', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'se', + 'sk', + 'sl', + 'sn', + 'sp', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'build-an-atom', + 'atomic-interactions', + 'states-of-matter-basics', + 'energy-forms-and-changes', + 'gas-properties'], + 'design_team': 'Yuen-ying Carpenter (lead designer, HTML5); Amy Rouinfar (lead designer, ' + 'HTML5); Paul Beale (lead designer, Java); Sarah McKagan (lead designer, ' + 'Java); Emily Moore (lead designer, Java); Noah Podolefsky (lead designer, ' + 'Java); John Blanco (lead developer); Aaron Davis (developer); Aadish Gupta ' + '(developer); Wendy Adams ; Jack Barbera; Amy Hanson; Kelly Lancaster; ' + 'Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/states-of-matter', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/states-of-matter/latest/states-of-matter-600.png'}, + { 'slug': 'states-of-matter-basics', + 'is_phet_studio': False, + 'title': 'States of Matter: Basics', + 'description': 'Heat, cool and compress atoms and molecules and watch as they change ' + 'between solid, liquid and gas phases.', + 'learning_goals': 'Describe characteristics of three states of matter: solid, liquid and ' + 'gas.
Predict how varying the temperature or pressure changes the ' + 'behavior of particles.
Compare particles in the three different ' + 'phases.
Explain freezing and melting with molecular level ' + 'detail.
Recognize that different substances have different ' + 'properties, including melting, freezing and boiling temperatures.', + 'subjects': ['physics', 'chemistry'], + 'topics': ['heat-and-thermo', 'general-chemistry'], + 'detail_topics': ['Atoms', 'Molecules', 'States of Matter'], + 'grades': ['elementary', 'middle', 'high'], + 'version': '1.2.23', + 'released': '2016-12-28', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'build-an-atom', + 'atomic-interactions', + 'states-of-matter', + 'energy-forms-and-changes', + 'gas-properties', + 'gases-intro'], + 'design_team': 'Yuen-ying Carpenter (lead designer, HTML5); Amy Rouinfar (lead designer, ' + 'HTML5); Paul Beale (lead designer, Java); Sarah McKagan (lead designer, ' + 'Java); Emily Moore (lead designer, Java); Noah Podolefsky (lead designer, ' + 'Java); John Blanco (lead developer); Aaron Davis (developer); Aadish Gupta ' + '(developer); Wendy Adams ; Jack Barbera; Amy Hanson; Kelly Lancaster; ' + 'Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/states-of-matter-basics', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/states-of-matter-basics/latest/states-of-matter-basics-600.png'}, + { 'slug': 'trig-tour', + 'is_phet_studio': False, + 'title': 'Trig Tour', + 'description': 'Take a tour of trigonometry using degrees or radians! Look for patterns in ' + 'the values and on the graph when you change the value of theta. Compare ' + 'the graphs of sine, cosine, and tangent.', + 'learning_goals': 'Define trig functions for negative angles and angles greater than 90 ' + 'degrees.
Translate between multiple representations of trig ' + 'functions: as sides of a right triangle inscribed in a unit circle, ' + 'graph of the function vs. angle, and numerical values of the ' + 'function.
Deduce the sign (+, -, 0) of a trig function for any ' + 'given angle without a calculator using the unit circle concept. ' + '
Estimate the value of trig functions for any given angle without a ' + 'calculator using the unit circle concept.
Define exact trig ' + 'functions for special angles using degrees or radians for angle ' + 'measures.', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Sines', 'Cosines', 'Trigonometry'], + 'grades': ['high', 'university'], + 'version': '1.1.9', + 'released': '2016-02-08', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'ef', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mg', + 'mi', + 'mn', + 'mr', + 'ms', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': [], + 'design_team': 'Amanda McGarry (lead); Michael Dubson (lead, developer); Jesse Greenberg ' + '(developer); Ariel Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/trig-tour', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/trig-tour/latest/trig-tour-600.png'}, + { 'slug': 'under-pressure', + 'is_phet_studio': False, + 'title': 'Under Pressure', + 'description': 'Explore pressure under and above water. See how pressure changes as you ' + 'change fluids, gravity, container shapes, and volume.', + 'learning_goals': 'Describe how pressure changes in air and water as a function of ' + 'depth.
Describe what variables affect pressure.
Predict ' + 'pressure in a variety of situations.', + 'subjects': ['physics', 'earth-science'], + 'topics': [], + 'detail_topics': ['Pressure', 'Density', 'Fluids'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.1.36', + 'released': '2014-04-07', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mk', + 'mn', + 'mr', + 'ms', + 'mt', + 'nl', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['density', 'buoyancy', 'buoyancy-basics'], + 'design_team': 'Sam Reid (lead, developer); John Blanco (developer); Bryce Gruneich; Noah ' + 'Podolefsky; Ariel Paul; Patricia Loeblein; Kathy Perkins; Rachel Pepper', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/under-pressure', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/under-pressure/latest/under-pressure-600.png'}, + { 'slug': 'unit-rates', + 'is_phet_studio': False, + 'title': 'Unit Rates', + 'description': 'Discover the unit rate while shopping for fruits, vegetables, and candy. ' + 'Construct a double number line and look for patterns. Challenge yourself ' + 'on the race track as you compare cars with different rates!', + 'learning_goals': 'Interpret ratios of mixed units (e.g. $/lb)
Define the unit rate ' + 'and determine a method to calculate it
Predict how changing the ' + 'numerator or denominator of a rate will affect the unit rate
Use ' + 'the double number line to reason about rates and solve real-world ' + 'problems
Develop strategies to use the unit rate to solve ' + 'problems
Compare unit rates between two simultaneous situations', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Ratios', 'Proportional Reasoning', 'Double Number Line'], + 'grades': ['middle', 'high'], + 'version': '1.1.5', + 'released': '2017-04-04', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_MA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'en_CA', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'nl', + 'ny', + 'om', + 'pl', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tn', + 'tr', + 'tw', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['proportion-playground', 'number-line-integers', 'ratio-and-proportion'], + 'design_team': 'Amy Rouinfar (lead designer); Chris Malley (developer); Dave Schmitz ' + '(developer); Amy Hanson ; Amanda McGarry; Susan Miller; Ariel Paul; Kathy ' + 'Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/unit-rates', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/unit-rates/latest/unit-rates-600.png'}, + { 'slug': 'vector-addition', + 'is_phet_studio': True, + 'title': 'Vector Addition', + 'description': 'Explore vectors in 1D or 2D, and discover how vectors add together. ' + 'Specify vectors in Cartesian or polar coordinates, and see the magnitude, ' + 'angle, and components of each vector. Experiment with vector equations and ' + 'compare vector sums and differences.', + 'learning_goals': 'Describe a vector in your own words
Explain a method to add ' + 'vectors
Compare and contrast the component styles
Decompose a ' + 'vector into components
Describe what happens to a vector when it is ' + 'multiplied by a scalar
Arrange vectors graphically to represent ' + 'vector addition or subtraction', + 'subjects': ['physics', 'math'], + 'topics': ['motion', 'math-concepts'], + 'detail_topics': ['Vectors', 'Vector Addition', 'Vector Components'], + 'grades': ['high', 'university'], + 'version': '1.2.4', + 'released': '2019-11-06', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lv', + 'mg', + 'mi', + 'mn', + 'mr', + 'ms', + 'nb', + 'nl', + 'ny', + 'om', + 'pl', + 'pr', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sn', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['vector-addition-equations', 'collision-lab', 'quantum-measurement'], + 'design_team': 'Brandon Li (developer); Chris Malley (developer); Martin Veillette ' + '(developer); Amy Rouinfar (lead designer); Michael Dubson (original ' + 'designer); Matthew Blackman; Diana López Tavares; Amanda McGarry; Ariel ' + 'Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/vector-addition', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/vector-addition/latest/vector-addition-600.png'}, + { 'slug': 'vector-addition-equations', + 'is_phet_studio': True, + 'title': 'Vector Addition: Equations', + 'description': 'Experiment with vector equations and compare vector sums and differences. ' + 'Customize the base vectors or explore scalar multiplication by adjusting ' + 'the coefficients in equation. Specify vectors in Cartesian or polar ' + 'coordinates, and view the magnitude, angle, and components of each vector.', + 'learning_goals': 'Describe what happens to a vector when it is multiplied by a ' + 'scalar
Arrange vectors graphically to represent vector addition or ' + 'subtraction
Compare the outcomes of each vector equation', + 'subjects': ['math'], + 'topics': ['math-concepts'], + 'detail_topics': ['Vectors', 'Vector Addition', 'Vector Components'], + 'grades': ['high', 'university'], + 'version': '1.2.4', + 'released': '2019-11-06', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fr', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ki', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lv', + 'mg', + 'mi', + 'mn', + 'ms', + 'nl', + 'ny', + 'om', + 'pl', + 'pr', + 'ps', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sk', + 'sl', + 'sn', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_HK', + 'zh_TW', + 'zu'], + 'related': ['vector-addition'], + 'design_team': 'Brandon Li (developer); Chris Malley (developer); Martin Veillette ' + '(developer); Amy Rouinfar (lead designer); Michael Dubson (original ' + 'designer); Matthew Blackman; Diana López Tavares; Amanda McGarry; Ariel ' + 'Paul; Kathy Perkins', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/vector-addition-equations', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/vector-addition-equations/latest/vector-addition-equations-600.png'}, + { 'slug': 'wave-interference', + 'is_phet_studio': False, + 'title': 'Wave Interference', + 'description': 'Make waves with a dripping faucet, audio speaker, or laser! Add a second ' + 'source to create an interference pattern. Put up a barrier to explore ' + 'single-slit diffraction and double-slit interference. Experiment with ' + 'diffraction through elliptical, rectangular, or irregular apertures.', + 'learning_goals': 'Make waves with water, sound, and light and see how they are ' + 'related.
Design an experiment to measure the speed of the ' + 'wave.
Create an interference pattern with two sources, and ' + 'determine the ways to change the pattern.
Find points of ' + 'constructive and destructive interference by eye and by using the ' + 'detectors.
Put up a barrier to see how the waves move through one ' + 'or two slits. What sort of pattern do the slits create? How can you ' + 'change this pattern?
For light, predict the locations of the ' + 'fringes that appear on the screen using d sin(θ) = mλ. Use ' + 'the tape measure to verify your predictions.
Explain how the ' + 'aperture geometry relates to the diffraction pattern.
Predict how ' + 'changing the wavelength or aperture size affects the diffraction ' + 'pattern.', + 'subjects': ['physics', 'earth-science'], + 'topics': ['sound-and-waves', 'light-and-radiation'], + 'detail_topics': ['Interference', 'Double Slit', 'Diffraction'], + 'grades': ['elementary', 'middle', 'high', 'university'], + 'version': '2.1.0', + 'released': '2019-01-30', + 'updated': '2026-08-28', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'ne', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sd', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': [ 'wave-on-a-string', + 'bending-light', + 'waves-intro', + 'fourier-making-waves', + 'quantum-wave-interference'], + 'design_team': 'Sam Reid (lead developer); Amy Rouinfar (lead designer); Noah Podolefsky ' + '(original designer); Wendy Adams; Diana López Tavares; Ariel Paul; Kathy ' + 'Perkins; Katie Woessner; Cheryl McCutchan (graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/wave-interference', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/wave-interference/latest/wave-interference-600.png'}, + { 'slug': 'wave-on-a-string', + 'is_phet_studio': True, + 'title': 'Wave on a String', + 'description': 'Explore the wonderful world of waves! Even observe a string vibrate in ' + 'slow motion. Wiggle the end of the string and make waves, or adjust the ' + 'frequency and amplitude of an oscillator.', + 'learning_goals': 'Discuss wave properties using common vocabulary.
Predict the ' + 'behavior of waves through varying medium and at reflective endpoints.', + 'subjects': ['physics', 'chemistry', 'earth-science', 'math'], + 'topics': ['sound-and-waves', 'general-chemistry', 'math-applications'], + 'detail_topics': ['Amplitude', 'Frequency', 'Waves'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.2.4', + 'released': '2014-09-18', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bo', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mi', + 'mk', + 'mn', + 'mr', + 'ms', + 'nb', + 'ne', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'se', + 'si', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['wave-interference', 'waves-intro'], + 'design_team': 'Ariel Paul (lead); Michael Dubson (lead, developer); Brett Fiedler (lead, ' + 'v1.2); Jonathan Olson (developer); Patricia Loeblein; Kathy Perkins; Amy ' + 'Rouinfar; Sharon Siman-Tov (graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/wave-on-a-string', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/wave-on-a-string/latest/wave-on-a-string-600.png'}, + { 'slug': 'waves-intro', + 'is_phet_studio': False, + 'title': 'Waves Intro', + 'description': 'Make waves with a dripping faucet, audio speaker, or laser! Adjust ' + 'frequency and amplitude, and observe the effects. Hear the sound produced ' + 'by the speaker, and discover what determines the color of light.', + 'learning_goals': 'Make waves with water, sound, and light and see how they are ' + 'related.
Discuss wave properties using common ' + 'vocabulary.
Explain how changing the frequency and amplitude ' + 'affects the characteristics of the wave.
Design an experiment to ' + 'measure the speed of the wave.', + 'subjects': ['physics', 'earth-science'], + 'topics': ['sound-and-waves', 'light-and-radiation'], + 'detail_topics': [], + 'grades': ['elementary', 'middle'], + 'version': '2.1.0', + 'released': '2019-09-24', + 'updated': '2026-08-28', + 'is_new': False, + 'locales': [ 'af', + 'am', + 'ar', + 'ar_LY', + 'ar_MA', + 'ar_SA', + 'az', + 'be', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'cy', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'hy', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'ki', + 'kk', + 'km', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'mt', + 'nb', + 'ne', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'sd', + 'sk', + 'sl', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'tk', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['wave-on-a-string', 'wave-interference'], + 'design_team': 'Sam Reid (lead developer); Amy Rouinfar (lead designer); Noah Podolefsky ' + '(original designer); Wendy Adams; Diana López Tavares; Ariel Paul; Kathy ' + 'Perkins; Katie Woessner; Cheryl McCutchan (graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/waves-intro', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/waves-intro/latest/waves-intro-600.png'}, + { 'slug': 'ph-scale', + 'is_phet_studio': True, + 'title': 'pH Scale', + 'description': 'Test the pH of things like coffee, spit, and soap to determine whether ' + 'each is acidic, basic, or neutral. Visualize the relative number of ' + 'hydroxide ions and hydronium ions in solution. Switch between logarithmic ' + 'and linear scales. Investigate whether changing the volume or diluting ' + 'with water affects the pH. Or you can design your own liquid!', + 'learning_goals': 'Determine if a liquid is acidic, basic, or neutral
Place acids or ' + 'bases in relative order
Describe on a molecular scale, with ' + 'illustrations, how the water equilibrium varies with pH
Determine ' + 'concentration of hydroxide, hydronium and water at a given ' + 'pH
Relate liquid color to pH
Predict (qualitatively and ' + 'quantitatively) how dilution and volume will affect the pH and ' + 'concentration of hydroxide, hydronium and water', + 'subjects': ['biology', 'chemistry', 'earth-science'], + 'topics': ['general-chemistry'], + 'detail_topics': ['pH', 'Concentration', 'Dilution'], + 'grades': ['middle', 'high', 'university'], + 'version': '1.7.6', + 'released': '2014-08-22', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_KW', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_ES', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'ga', + 'gl', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'in', + 'is', + 'it', + 'iw', + 'ja', + 'ka', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lo', + 'lt', + 'lv', + 'mi', + 'mk', + 'mn', + 'mr', + 'ms', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'or', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sn', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'ti', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['ph-scale-basics', 'acid-base-solutions'], + 'design_team': 'Yuen-ying Carpenter (lead); Chris Malley (developer); Marla Schulz ' + '(developer); Julia Chamberlain; Patricia Loeblein; Diana López Tavares; ' + 'Emily B. Moore; Ariel Paul; Kathy Perkins; Amy Rouinfar; Nancy Salpepi; ' + 'Taliesin Smith; Sharon Siman-Tov (graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/ph-scale', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/ph-scale/latest/ph-scale-600.png'}, + { 'slug': 'ph-scale-basics', + 'is_phet_studio': True, + 'title': 'pH Scale: Basics', + 'description': 'Test the pH of everyday liquids such as coffee, spit, and soap to ' + 'determine whether each is acidic, basic, or neutral. Investigate how ' + 'adding more of a liquid or diluting with water affects pH.', + 'learning_goals': 'Determine if a solution is acidic, basic or neutral
Place acids or ' + 'bases in order of relative acidity or basicity
Relate liquid color ' + 'to pH
Predict how solution volume or dilution with water will ' + 'affect the pH of acids or bases', + 'subjects': ['chemistry'], + 'topics': ['general-chemistry'], + 'detail_topics': ['pH', 'Dilution', 'Acids'], + 'grades': ['middle'], + 'version': '1.7.5', + 'released': '2014-10-29', + 'updated': '2026-06-24', + 'is_new': False, + 'locales': [ 'am', + 'ar', + 'ar_KW', + 'ar_MA', + 'ar_SA', + 'az', + 'bg', + 'bn', + 'bs', + 'ca', + 'cs', + 'da', + 'de', + 'el', + 'en', + 'es', + 'es_MX', + 'es_PE', + 'et', + 'eu', + 'fa', + 'fa_DA', + 'fi', + 'fo', + 'fr', + 'ga', + 'gu', + 'ha', + 'hi', + 'hr', + 'ht', + 'hu', + 'ig', + 'is', + 'it', + 'iw', + 'ja', + 'kk', + 'kn', + 'ko', + 'ku', + 'lg', + 'lt', + 'lv', + 'mi', + 'mn', + 'mr', + 'ms', + 'nb', + 'nl', + 'nn', + 'ny', + 'om', + 'pl', + 'ps', + 'pt', + 'pt_BR', + 'ro', + 'ru', + 'rw', + 'si', + 'sk', + 'sl', + 'sn', + 'sq', + 'sr', + 'st', + 'sv', + 'sw', + 'ta', + 'te', + 'tg', + 'th', + 'ti', + 'tk', + 'tn', + 'tr', + 'tw', + 'uk', + 'ur', + 'uz', + 'vi', + 'yo', + 'zh_CN', + 'zh_TW', + 'zu'], + 'related': ['acid-base-solutions', 'ph-scale'], + 'design_team': 'Yuen-ying Carpenter (lead); Chris Malley (developer); Marla Schulz ' + '(developer); Julia Chamberlain; Patricia Loeblein; Diana López Tavares; ' + 'Emily B. Moore; Ariel Paul; Katherine Perkins; Amy Rouinfar; Nancy ' + 'Salpepi; Taliesin Smith; Sharon Siman-Tov (graphic arts)', + 'teachers_guide': True, + 'upstream_page': 'https://phet.colorado.edu/en/simulations/ph-scale-basics', + 'screenshot_source': 'https://phet.colorado.edu/sims/html/ph-scale-basics/latest/ph-scale-basics-600.png'}] diff --git a/sites/phet_simulations/static/css/style.css b/sites/phet_simulations/static/css/style.css new file mode 100644 index 00000000..c66d7f25 --- /dev/null +++ b/sites/phet_simulations/static/css/style.css @@ -0,0 +1,1351 @@ +/* PhET Interactive Simulations — mirror reskin + Palette + typography derived from phet.colorado.edu (CC-BY) */ + +:root { + --phet-magenta: #e01e5a; + --phet-magenta-dark: #b6184a; + --phet-navy: #2a326a; + --phet-navy-dark: #1f2552; + --phet-blue: #15337f; + --phet-blue-hover: #2345a1; + --phet-orange: #d36a04; + --phet-purple: #521764; + --phet-cyan: #00a1cc; + + --c-bg: #ffffff; + --c-bg-alt: #f3f5f3; + --c-bg-footer: #f0f0f0; + --c-border: #d8dde3; + --c-border-strong: #b8bfc6; + --c-text: #1f2933; + --c-muted: #5b6470; + --c-link: var(--phet-blue); + --c-link-hover: var(--phet-magenta); + --c-success: #2e7d32; + --c-error: #c0392b; + + --radius: 3px; + --shadow-card: 1px 1px 4px rgba(0,0,0,0.25); + --shadow-card-hover: 2px 2px 8px rgba(0,0,0,0.35); + --max-width: 1180px; +} + +* { box-sizing: border-box; } +html, body { margin: 0; padding: 0; } +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + color: var(--c-text); + background: var(--c-bg); + line-height: 1.5; + font-size: 14px; +} +a { color: var(--c-link); text-decoration: none; } +a:hover { color: var(--c-link-hover); text-decoration: underline; } +h1, h2, h3, h4 { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } +.container { max-width: var(--max-width); margin: 0 auto; padding: 0 1.25rem; } + +/* === Header (real PhET look: single white bar) === */ +.topbar { background: #fff; border-bottom: 1px solid var(--c-border); } +.topbar-inner { + display: flex; align-items: center; gap: 1.5rem; + padding: 0.5rem 1.25rem; + max-width: var(--max-width); margin: 0 auto; + min-height: 60px; +} +.brand { display: flex; align-items: center; gap: 0.65rem; text-decoration: none; flex-shrink: 0; } +.brand:hover { text-decoration: none; } +.brand-phet { height: 42px; width: auto; display: block; } +.brand-cu { height: 38px; width: auto; display: block; } +.brand-divider { + width: 1px; height: 36px; background: var(--c-border); + display: inline-block; +} + +.primary-nav { + display: flex; + gap: 0; + margin-left: auto; + align-items: stretch; +} +.nav-item { + position: relative; + display: flex; align-items: center; +} +.nav-item > a { + color: #4a525c; + text-transform: uppercase; + font-weight: 700; + font-size: 0.78rem; + letter-spacing: 0.06em; + padding: 1.1rem 0.95rem; + text-decoration: none; + display: block; + white-space: nowrap; +} +.nav-item > a:hover { color: var(--phet-magenta); text-decoration: none; } +.nav-item.active > a { color: var(--phet-magenta); } +.nav-item.active > a::before { + content: ""; position: absolute; + top: 0; left: 50%; transform: translateX(-50%); + width: 22px; height: 3px; + background: var(--phet-magenta); +} +.nav-dropdown { + display: none; + position: absolute; + top: 100%; left: 0; + background: #fff; + border: 1px solid var(--c-border); + box-shadow: 0 4px 14px rgba(0,0,0,0.12); + min-width: 180px; + padding: 0.5rem 0; + z-index: 100; +} +.nav-item:hover .nav-dropdown, +.nav-item:focus-within .nav-dropdown { display: block; } +.nav-dropdown a { + display: block; + padding: 0.55rem 1.1rem; + color: var(--c-text); + font-size: 0.88rem; + text-decoration: none; + text-transform: none; + letter-spacing: 0; + font-weight: 400; +} +.nav-dropdown a:hover { background: var(--c-bg-alt); color: var(--phet-magenta); } +.nav-dropdown hr { + border: 0; border-top: 1px solid var(--c-border); + margin: 0.4rem 0; +} + +.search-form { + display: flex; gap: 0; + margin-left: 0.5rem; + align-items: center; + position: relative; +} +.search-form input[type=search] { + position: absolute; + right: 100%; + top: 50%; transform: translateY(-50%); + padding: 0.35rem 0.55rem; + border: 1px solid var(--c-border-strong); + border-right: 0; + border-radius: var(--radius) 0 0 var(--radius); + width: 0; padding-left: 0; padding-right: 0; + font-size: 0.85rem; + outline: none; + background: #fff; + visibility: hidden; + transition: width 0.18s, padding 0.18s; +} +.search-form:hover input[type=search], +.search-form:focus-within input[type=search] { + width: 200px; + padding-left: 0.55rem; padding-right: 0.55rem; + visibility: visible; +} +.search-form input[type=search]:focus { border-color: var(--phet-magenta); } +.search-form button { + background: transparent; + color: var(--c-text); + border: 0; + padding: 0.5rem; + border-radius: var(--radius); + cursor: pointer; + display: flex; align-items: center; +} +.search-form button:hover { color: var(--phet-magenta); } + +.user-menu { display: flex; gap: 0.5rem; align-items: center; } +.user-link { + color: var(--c-muted); + font-size: 0.78rem; + text-transform: uppercase; + letter-spacing: 0.05em; + font-weight: 700; + display: flex; align-items: center; + padding: 0.3rem 0.5rem; +} +.user-link:hover { color: var(--phet-magenta); text-decoration: none; } +.user-link.muted { font-weight: 400; } + +/* Flashes */ +.flashes { margin-top: 1rem; } +.flash { + padding: 0.75rem 1rem; margin-bottom: 0.5rem; + border-radius: var(--radius); border: 1px solid; +} +.flash-success { background: #e8f5e9; border-color: var(--c-success); color: var(--c-success); } +.flash-error { background: #fdecea; border-color: var(--c-error); color: var(--c-error); } + +/* Layout */ +.main { padding: 1.5rem 0 3rem; } +.main:not(:empty) { min-height: 40vh; } +.home-main { padding: 1.5rem 0 2rem; } +.page-title { margin: 0 0 0.4rem; font-size: 1.65rem; color: var(--phet-orange); font-weight: normal; } +.page-subtitle { color: var(--c-muted); margin: 0 0 1.5rem; font-size: 0.95rem; } +.row { margin: 2rem 0; } +.row-title { + font-size: 1.15rem; margin: 0 0 1rem; + color: var(--phet-blue); font-weight: 700; + border-bottom: 2px solid var(--phet-blue); padding-bottom: 0.35rem; +} +.empty-state { + background: var(--c-bg-alt); padding: 1rem 1.25rem; + border-radius: var(--radius); color: var(--c-muted); +} +.breadcrumb { color: var(--c-muted); font-size: 0.88rem; margin-bottom: 0.75rem; } +.breadcrumb a { color: var(--phet-blue); } + +/* Buttons — PhET style: small, uppercase, bold */ +.btn { + display: inline-block; + padding: 0.45rem 0.95rem; + border-radius: var(--radius); + font-weight: 700; + cursor: pointer; + border: 1px solid transparent; + text-decoration: none; + font-size: 0.78rem; + text-transform: uppercase; + letter-spacing: 0.05em; + line-height: 1.2; +} +.btn-primary { background: var(--phet-navy); color: #fff; } +.btn-primary:hover { background: var(--phet-navy-dark); text-decoration: none; color: #fff; } +.btn-ghost { + background: #fff; color: var(--phet-navy); + border-color: var(--phet-navy); +} +.btn-ghost:hover { background: var(--phet-navy); color: #fff; text-decoration: none; } +.btn-block { display: block; width: 100%; text-align: center; } +.btn-play { + background: var(--phet-magenta); color: #fff; font-size: 0.9rem; + padding: 0.65rem 1.4rem; +} +.btn-play:hover { background: var(--phet-magenta-dark); color: #fff; } + +/* === Educator banner (thin pink strip above hero) === */ +.educator-banner { + background: #fde4ee; + color: #6b1734; + font-size: 0.85rem; + padding: 0.55rem 2.5rem 0.55rem 1.25rem; + text-align: center; + border-bottom: 1px solid #f8cfde; + position: relative; +} +.educator-banner a { color: var(--phet-magenta); font-weight: 700; text-decoration: underline; } +.educator-close { + position: absolute; + right: 1rem; top: 50%; transform: translateY(-50%); + background: transparent; + border: 0; + color: #6b1734; + cursor: pointer; + padding: 0.3rem; + display: flex; align-items: center; + border-radius: 50%; + line-height: 0; +} +.educator-close:hover { background: rgba(0,0,0,0.05); color: var(--phet-magenta); } + + +/* === Hero (real photo top banner) === */ +.hero-photo { + position: relative; + background: + linear-gradient(90deg, rgba(0,0,0,0.15) 0%, rgba(0,0,0,0.4) 60%, rgba(0,0,0,0.5) 100%), + url('../images/ui/hero-simulations.jpg') center/cover no-repeat; + color: #fff; + min-height: 420px; + display: flex; align-items: center; + margin: 0; + overflow: hidden; +} +.hero-photo-inner { + position: relative; + max-width: var(--max-width); margin: 0 auto; + padding: 4rem 1.25rem; + width: 100%; + display: flex; justify-content: flex-end; +} +.hero-photo-text { max-width: 540px; text-align: left; } +.hero-photo h1 { + font-size: 2.4rem; margin: 0 0 1.5rem; + font-weight: normal; line-height: 1.15; + text-shadow: 0 2px 12px rgba(0,0,0,0.6); +} +.hero-photo .btn { + background: rgba(0,0,0,0.65); + border: 1px solid #fff; + color: #fff; + border-radius: 10px; + padding: 0.75rem 1.5rem; + font-size: 0.82rem; +} +.hero-photo .btn:hover { background: var(--phet-magenta); border-color: var(--phet-magenta); color: #fff; text-decoration: none; } + +/* Secondary hero — "Shouldn't All Students Experience STEM?" */ +.hero-stem { + position: relative; + background: + linear-gradient(90deg, rgba(0,0,0,0.15) 0%, rgba(0,0,0,0.4) 60%, rgba(0,0,0,0.55) 100%), + url('../images/ui/hero-accessibility.jpg') center/cover no-repeat; + color: #fff; + min-height: 320px; + display: flex; align-items: center; + margin: 0; +} +.hero-stem-inner { + max-width: var(--max-width); margin: 0 auto; + padding: 3rem 1.25rem; + width: 100%; + display: flex; justify-content: flex-end; +} +.hero-stem-text { max-width: 460px; text-align: left; } +.hero-stem h2 { + font-size: 1.8rem; margin: 0 0 0.8rem; + font-weight: normal; line-height: 1.2; + text-shadow: 0 2px 12px rgba(0,0,0,0.6); +} +.hero-stem p { margin: 0 0 1.25rem; opacity: 0.95; font-size: 0.92rem; } +.hero-stem .btn { + background: rgba(0,0,0,0.6); + border: 1px solid #fff; + color: #fff; + border-radius: 10px; + padding: 0.65rem 1.3rem; + font-size: 0.78rem; +} +.hero-stem .btn:hover { background: var(--phet-magenta); border-color: var(--phet-magenta); color: #fff; } + +/* Home-page "Supported by" + DONATE NOW block */ +.home-supported { + background: #fff; + text-align: center; + padding: 2.5rem 1.25rem 3rem; + border-top: 1px solid var(--c-border); +} +.home-supported-label { + font-size: 0.85rem; color: var(--c-text); + margin-bottom: 1rem; +} +.home-supported-logo { + font-family: Georgia, serif; font-style: italic; + font-size: 1.3rem; color: var(--phet-navy); font-weight: 700; + display: inline-block; margin-bottom: 0.5rem; +} +.home-supported-foot { + font-size: 0.85rem; color: var(--c-muted); + margin: 0.5rem auto 1.25rem; max-width: 480px; +} +.home-supported .donate-cta { margin-top: 0; } + +/* "Over 1.8 billion simulations delivered" banner */ +.stats-banner { + background: #f0f0f0; + padding: 1rem 1.25rem; + text-align: center; + font-size: 1rem; + color: var(--c-text); + font-weight: 600; + border-bottom: 1px solid var(--c-border); +} +.stats-banner strong { color: var(--phet-magenta); font-weight: 800; } + +/* legacy .hero retained for sub-pages (orange accent) */ +.hero { + background: var(--c-bg-alt); + padding: 2rem; + border-left: 5px solid var(--phet-magenta); + margin-bottom: 2rem; +} +.hero h1 { margin: 0 0 0.5rem; font-size: 1.8rem; color: var(--phet-orange); font-weight: normal; } +.hero p { margin: 0 0 1rem; max-width: 720px; } +.hero-actions { display: flex; gap: 0.75rem; flex-wrap: wrap; } + +/* === Pastel subject squares (homepage) === */ +.subjects-pastel { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 1rem; + margin: 2rem 0; +} +/* reviewer fix: the five-column grid did not reflow between 761px and the + desktop width, so the last card overhung the viewport at 768px */ +@media (max-width: 1000px) { .subjects-pastel { grid-template-columns: repeat(3, 1fr); } } +@media (max-width: 760px) { .subjects-pastel { grid-template-columns: repeat(2, 1fr); } } +.subject-pastel { + aspect-ratio: 1; + border-radius: 14px; + background: var(--ps-bg, #eee); + display: flex; flex-direction: column; + align-items: center; justify-content: center; + text-decoration: none; color: var(--c-text); + padding: 1.25rem; + transition: transform 0.18s; +} +.subject-pastel:hover { transform: translateY(-3px); text-decoration: none; } +.subject-pastel img { width: 56px; height: 56px; margin-bottom: 0.85rem; } +.subject-pastel-name { + font-weight: 700; font-size: 0.85rem; + text-transform: uppercase; letter-spacing: 0.05em; + text-align: center; + color: var(--c-text); +} +/* pastel color palette by subject slug */ +.ps-physics { background: #f7d6da; } +.ps-math { background: #fdeec0; } +.ps-chemistry { background: #cbe9f3; } +.ps-earth-science { background: #d4e8c8; } +.ps-biology { background: #e7d4ea; } + +/* === Teaching Resources dark photo callout === */ +.teaching-callout { + position: relative; + background: + linear-gradient(90deg, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.5) 45%, rgba(0,0,0,0.1) 100%), + url('../images/ui/hero-teachers.jpg') center/cover no-repeat; + color: #fff; + padding: 3.5rem 2rem; + margin: 0; + min-height: 320px; + display: flex; align-items: center; + overflow: hidden; +} +.teaching-callout-inner { + max-width: var(--max-width); margin: 0 auto; + width: 100%; +} +.teaching-callout-text { max-width: 460px; } +.teaching-callout h2 { + font-size: 1.9rem; margin: 0 0 0.85rem; + font-weight: normal; line-height: 1.2; + text-shadow: 0 2px 8px rgba(0,0,0,0.4); +} +.teaching-callout p { margin: 0 0 1.25rem; opacity: 0.95; font-size: 0.9rem; line-height: 1.5; } +.teaching-callout .btn { + background: rgba(0,0,0,0.6); + border: 1px solid #fff; color: #fff; + border-radius: 10px; padding: 0.65rem 1.5rem; + font-size: 0.78rem; +} +.teaching-callout .btn:hover { background: var(--phet-magenta); border-color: var(--phet-magenta); color: #fff; } + +/* === INTERACT DISCOVER LEARN stats row === */ +.interact-section { + text-align: center; + margin: 3rem 0; +} +.interact-heading img { + height: 80px; margin: 0 auto 1.5rem; + display: block; +} +.interact-stats { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 2rem; + max-width: 800px; + margin: 1rem auto 2rem; +} +@media (max-width: 700px) { .interact-stats { grid-template-columns: 1fr; } } +.interact-stat { text-align: center; } +.interact-stat-num { + font-size: 2.4rem; + font-weight: 700; + color: var(--c-text); + display: block; margin-bottom: 0.25rem; +} +.interact-stat-label { + color: var(--c-muted); + font-size: 0.95rem; +} +.interact-stat img { height: 40px; margin-top: 0.5rem; } + +/* About PhET paragraph */ +.about-blurb { + max-width: 740px; + margin: 2rem auto 3rem; + text-align: center; + color: var(--c-muted); + font-size: 0.95rem; + line-height: 1.6; +} +.about-blurb a { color: var(--phet-magenta); } + +/* === Sim card === */ +.sim-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(155px, 1fr)); + gap: 1.5rem 1rem; +} +.sim-grid-small { + grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); + gap: 1.25rem 0.85rem; +} +.sim-card { + background: transparent; + display: flex; flex-direction: column; + position: relative; + border: 0; +} +.sim-thumb { + display: block; + position: relative; + aspect-ratio: 3 / 2; + overflow: hidden; + box-shadow: var(--shadow-card); + background: #f4f6f8; + margin-bottom: 6px; +} +.sim-thumb:hover { box-shadow: var(--shadow-card-hover); } +.sim-thumb img { + width: 100%; height: 100%; + object-fit: cover; + display: block; +} +.sim-thumb-placeholder { + width: 100%; height: 100%; + display: flex; align-items: center; justify-content: center; + background: linear-gradient(135deg, var(--ph-color-a, #2a326a) 0%, var(--ph-color-b, #15337f) 100%); + color: #fff; + font-weight: 600; + font-size: 0.85rem; + text-align: center; + padding: 0.6rem; + line-height: 1.2; +} +.sim-type-badge { + position: absolute; + bottom: 6px; left: 6px; + width: 26px; height: 26px; + border-radius: 50%; + background: var(--phet-magenta); + color: #fff; + font-size: 0.65rem; + font-weight: 800; + display: flex; align-items: center; justify-content: center; + box-shadow: 0 1px 3px rgba(0,0,0,0.3); +} +.sim-meta { padding: 0; } +.sim-title { margin: 0 0 0.2rem; font-size: 0.92rem; font-weight: 600; line-height: 1.25; } +.sim-title a { color: var(--c-text); text-decoration: none; } +.sim-title a:hover { color: var(--phet-magenta); text-decoration: underline; } +.sim-desc { color: var(--c-muted); font-size: 0.78rem; margin: 0 0 0.3rem; line-height: 1.35; } +.sim-tags { list-style: none; padding: 0; margin: 0.25rem 0 0; display: flex; flex-wrap: wrap; gap: 0.25rem; } +.sim-tag { + font-size: 0.68rem; + background: transparent; + color: var(--phet-blue); + padding: 0; +} +.sim-tag::after { content: ","; } +.sim-tag:last-child::after { content: ""; } +.badge { + display: inline-block; font-size: 0.65rem; + padding: 0.1rem 0.45rem; border-radius: 2px; + margin-right: 0.25rem; + text-transform: uppercase; + letter-spacing: 0.05em; + font-weight: 700; +} +.badge-new { background: var(--phet-magenta); color: #fff; } +.badge-featured { background: var(--phet-orange); color: #fff; } +.badge-html5 { background: var(--phet-blue); color: #fff; } +.badge-version { background: var(--c-bg-alt); color: var(--c-muted); } + +/* Filter bar */ +.filter-bar { + display: flex; flex-wrap: wrap; + gap: 0.75rem; align-items: flex-end; + background: var(--c-bg-alt); + padding: 1rem 1.25rem; + border-radius: var(--radius); + border-left: 4px solid var(--phet-magenta); + margin-bottom: 1.5rem; +} +.filter-bar label { + display: flex; flex-direction: column; + font-size: 0.78rem; color: var(--c-muted); + gap: 0.2rem; text-transform: uppercase; letter-spacing: 0.04em; +} +.filter-bar select { + padding: 0.4rem 0.6rem; + border: 1px solid var(--c-border-strong); + border-radius: var(--radius); + background: #fff; + min-width: 160px; + font-size: 0.85rem; +} +.search-form-wide { display: flex; gap: 0.5rem; margin: 1rem 0 2rem; } +.search-form-wide input { + flex: 1; + padding: 0.6rem 0.8rem; + border: 1px solid var(--c-border-strong); + border-radius: var(--radius); +} + +/* Pager */ +.pager { display: flex; gap: 1rem; align-items: center; justify-content: center; margin: 2rem 0; } +.pager-status { color: var(--c-muted); font-size: 0.9rem; } +.pager a { + color: var(--phet-blue); font-weight: 700; + text-transform: uppercase; font-size: 0.8rem; letter-spacing: 0.05em; +} + +/* === Browse-by-subject grouping (real PhET) === */ +.browse-hero { + background: + linear-gradient(180deg, rgba(180,225,245,0.0) 0%, rgba(180,225,245,0.0) 100%), + url('../images/ui/banner-underwater.png') center/cover no-repeat, + linear-gradient(180deg, #b3e0f5 0%, #cdedf7 100%); + padding: 2.5rem 1.25rem; + text-align: center; + margin-bottom: 0; + position: relative; + overflow: hidden; + min-height: 140px; + display: flex; align-items: center; justify-content: center; +} +.browse-hero h1 { + margin: 0; + font-size: 2.2rem; + color: var(--c-text); + font-weight: 700; + text-shadow: 0 1px 4px rgba(255,255,255,0.6); + position: relative; + z-index: 1; +} +.browse-subtabs { + display: flex; justify-content: center; gap: 2.5rem; + background: #fff; + border-bottom: 1px solid var(--c-border); + padding: 0.75rem 1.25rem; + margin-bottom: 2rem; +} +.browse-subtab { + font-size: 0.95rem; + color: var(--c-muted); + font-weight: 700; + text-transform: capitalize; + position: relative; padding: 0.3rem 0; + text-decoration: none; +} +.browse-subtab.active { color: var(--c-text); } +.browse-subtab.active::after { + content: ""; position: absolute; + left: 0; right: 0; bottom: -0.8rem; + height: 3px; background: var(--phet-magenta); +} + +/* === Filter sidebar layout === */ +.browse-layout { + display: grid; + grid-template-columns: 240px 1fr; + gap: 2rem; + max-width: var(--max-width); margin: 0 auto; + padding: 1.5rem 1.25rem; +} +@media (max-width: 800px) { .browse-layout { grid-template-columns: 1fr; } } + +.filter-sidebar { + font-size: 0.85rem; +} +.filter-group { margin-bottom: 1rem; border-bottom: 1px solid var(--c-border); padding-bottom: 0.6rem; } +.filter-group > summary { + cursor: pointer; + font-weight: 700; + text-transform: uppercase; + font-size: 0.78rem; + letter-spacing: 0.05em; + color: var(--c-text); + padding: 0.5rem 0; + list-style: none; + outline: none; + display: flex; align-items: center; justify-content: space-between; +} +.filter-group > summary::-webkit-details-marker { display: none; } +.filter-group > summary::after { + content: "▾"; + color: var(--c-muted); + font-size: 0.7rem; + transition: transform 0.2s; +} +.filter-group[open] > summary::after { transform: rotate(180deg); } +.filter-group ul { + list-style: none; padding: 0.3rem 0 0 0.15rem; + margin: 0; +} +.filter-group li { margin: 0.25rem 0; } +.filter-group label { + display: flex; align-items: flex-start; gap: 0.45rem; + font-size: 0.83rem; + color: var(--c-text); + cursor: pointer; + padding: 0.15rem 0; +} +.filter-group label:hover { color: var(--phet-magenta); } +.filter-group input[type=checkbox], +.filter-group input[type=radio] { + margin-top: 0.18rem; + accent-color: var(--phet-magenta); +} +.filter-group .sub-group { + margin-left: 0.9rem; + padding-left: 0.5rem; + border-left: 2px solid var(--c-border); + margin-bottom: 0.35rem; +} +.filter-group .sub-group-title { + font-weight: 700; + font-size: 0.78rem; + color: var(--phet-blue); + margin: 0.35rem 0 0.2rem; +} +.filter-clear { + display: block; + margin: 1rem 0; + padding: 0.45rem 0.9rem; + border: 1px solid var(--c-border-strong); + background: #fff; color: var(--c-text); + text-align: center; + font-size: 0.78rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.04em; + border-radius: 3px; + cursor: pointer; + width: 100%; +} +.filter-clear:hover { border-color: var(--phet-magenta); color: var(--phet-magenta); text-decoration: none; } + +.browse-results {} +.browse-results-head { + display: flex; align-items: center; gap: 0.75rem; + margin-bottom: 1rem; + border-bottom: 1px solid var(--c-border); + padding-bottom: 0.75rem; +} +.results-count { + font-size: 1.05rem; font-weight: 700; + color: var(--c-text); +} +.results-active-chips { + display: flex; gap: 0.4rem; flex-wrap: wrap; + margin-left: 0.75rem; +} +.results-chip { + display: inline-flex; align-items: center; gap: 0.3rem; + background: var(--phet-blue); + color: #fff; + font-size: 0.72rem; + padding: 0.15rem 0.45rem; + border-radius: 2px; + text-transform: uppercase; + letter-spacing: 0.04em; + font-weight: 700; +} +.results-chip a { + color: #fff; opacity: 0.85; + margin-left: 0.15rem; +} +.results-chip a:hover { opacity: 1; text-decoration: none; } +.results-sort { + margin-left: auto; + display: flex; align-items: center; gap: 0.4rem; +} +.results-sort label { font-size: 0.78rem; color: var(--c-muted); text-transform: uppercase; } +.results-sort select { + padding: 0.3rem 0.6rem; + border: 1px solid var(--c-border-strong); + border-radius: 3px; + background: #fff; + font-size: 0.85rem; +} + +.subject-section { margin: 0 0 2.5rem; padding: 0 1.25rem; } +.subject-section-head { + display: flex; align-items: center; gap: 0.5rem; + margin-bottom: 1rem; + padding-bottom: 0.4rem; + border-bottom: 1px solid var(--c-border); +} +.subject-section-head h2 { + margin: 0; font-size: 1.1rem; font-weight: 700; color: var(--c-text); +} +.subject-section-head .more { + margin-left: auto; + color: var(--phet-magenta); + font-size: 0.85rem; + font-weight: 700; + text-decoration: none; +} +.subject-section-head .more:hover { text-decoration: underline; } + +/* Language dots under sim cards (real PhET shows colored squares) */ +.lang-dots { + display: flex; gap: 2px; margin-top: 4px; + flex-wrap: wrap; +} +.lang-dot { + width: 10px; height: 10px; + background: var(--phet-magenta); + border-radius: 1px; + display: inline-block; +} +.lang-dot.muted { background: #d8dde3; } + +/* === Sim detail === */ +.sim-detail-head { margin-bottom: 1.5rem; } +.sim-detail-head h1 { margin: 0 0 0.5rem; font-size: 1.9rem; color: var(--phet-orange); font-weight: normal; } +.sim-detail-tagline { color: var(--c-muted); font-size: 1.02rem; margin: 0 0 0.75rem; } +.badge-row { list-style: none; padding: 0; margin: 0; display: flex; gap: 0.25rem; flex-wrap: wrap; } +.sim-detail-body { display: grid; grid-template-columns: 2.4fr 1fr; gap: 2rem; } +@media (max-width: 800px) { .sim-detail-body { grid-template-columns: 1fr; } } +.sim-detail-frame { + position: relative; + aspect-ratio: 3 / 2; + background: #f4f6f8; + box-shadow: var(--shadow-card-hover); + display: flex; align-items: center; justify-content: center; + flex-direction: column; gap: 1rem; + margin-bottom: 1.5rem; + overflow: hidden; +} +.sim-detail-frame img { + width: 100%; height: 100%; + object-fit: cover; display: block; +} +.sim-detail-frame-placeholder { + width: 100%; height: 100%; + background: linear-gradient(135deg, var(--phet-navy) 0%, var(--phet-blue) 100%); + color: #fff; + display: flex; align-items: center; justify-content: center; + font-size: 1.4rem; font-weight: 700; + text-align: center; padding: 1rem; +} +.sim-detail-play-overlay { + position: absolute; + inset: 0; + background: rgba(0,0,0,0.0); + display: flex; align-items: center; justify-content: center; + transition: background 0.2s; + cursor: pointer; + text-decoration: none; +} +.sim-detail-play-overlay:hover { background: rgba(0,0,0,0.4); text-decoration: none; } +.sim-detail-play-overlay::before { + content: "▶ PLAY"; + background: var(--phet-magenta); + color: #fff; + padding: 0.85rem 1.8rem; + font-weight: 800; + letter-spacing: 0.08em; + border-radius: var(--radius); + font-size: 1rem; + box-shadow: 0 4px 12px rgba(0,0,0,0.4); + opacity: 0; + transition: opacity 0.2s; +} +.sim-detail-play-overlay:hover::before { opacity: 1; } +.sim-detail-section { margin: 1.75rem 0; } +.sim-detail-section h2 { + font-size: 1.05rem; + color: var(--phet-blue); + font-weight: 700; + border-bottom: 2px solid var(--phet-blue); + padding-bottom: 0.3rem; + margin: 0 0 0.75rem; +} +.topic-list, .activity-list { padding-left: 1.5rem; } +.activity-list li { margin-bottom: 0.5rem; } +.activity-meta { color: var(--c-muted); font-size: 0.85rem; } +/* Tab nav on detail page */ +.detail-tabs { + display: flex; gap: 2rem; + border-bottom: 1px solid var(--c-border); + margin: 1.5rem 0; + padding: 0; +} +.detail-tab { + font-size: 0.95rem; + color: var(--c-muted); + font-weight: 700; + padding: 0.5rem 0; + text-decoration: none; + position: relative; + cursor: pointer; + background: transparent; + border: 0; +} +.detail-tab.active { color: var(--c-text); } +.detail-tab.active::after { + content: ""; position: absolute; + left: 0; right: 0; bottom: -1px; + height: 3px; background: var(--phet-magenta); +} +.detail-tab:hover { color: var(--phet-magenta); text-decoration: none; } + +.share-row { + display: flex; gap: 0.5rem; + margin: 0.75rem 0 1rem; +} +.share-row a { + width: 28px; height: 28px; + display: flex; align-items: center; justify-content: center; + background: var(--c-bg-alt); + color: var(--phet-navy); + border-radius: 3px; +} +.share-row a:hover { background: var(--phet-magenta); color: #fff; } + +.supported-by { + background: #fff; + border: 1px solid var(--c-border); + padding: 1.25rem 1rem; + text-align: center; + margin-bottom: 1rem; +} +.supported-by-label { + font-size: 0.78rem; + color: var(--c-muted); + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; + margin-bottom: 0.75rem; + display: block; +} +.supported-by-logo { + font-family: Georgia, serif; + font-style: italic; + font-size: 1.3rem; + color: var(--phet-navy); + font-weight: 700; +} +.supported-by-foot { + font-size: 0.78rem; + color: var(--c-muted); + margin-top: 0.75rem; + line-height: 1.4; +} + +/* Explore More + DONATE bottom CTA */ +.explore-more { + background: #fde4ee; + padding: 1.5rem 2rem; + margin: 2.5rem 0 0; + text-align: center; + border-radius: 4px; +} +.explore-more h3 { + font-size: 0.78rem; + color: var(--phet-magenta); + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 700; + margin: 0 0 0.4rem; +} +.explore-more-links { font-size: 1.05rem; font-weight: 700; color: var(--c-text); } +.explore-more-links a { color: var(--c-text); } +.explore-more-links a:hover { color: var(--phet-magenta); text-decoration: none; } +.donate-cta { + background: var(--phet-magenta); color: #fff; + padding: 0.85rem 2.5rem; + font-weight: 700; font-size: 0.9rem; + border-radius: 4px; + text-transform: uppercase; letter-spacing: 0.05em; + display: inline-block; + margin-top: 1rem; +} +.donate-cta:hover { background: var(--phet-magenta-dark); color: #fff; text-decoration: none; } + +.side-card { + background: #fff; + border: 1px solid var(--c-border); + border-top: 3px solid var(--phet-magenta); + border-radius: 0; + padding: 1rem 1.2rem; + margin-bottom: 1rem; +} +.side-card h3 { + margin: 0 0 0.6rem; + font-size: 0.78rem; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--phet-navy); + font-weight: 700; +} +.side-card ul { margin: 0; padding: 0; list-style: none; } +.side-card li { padding: 0.2rem 0; font-size: 0.88rem; border-bottom: 1px solid #e1e6eb; } +.side-card li:last-child { border: 0; } +.lang-list { max-height: 220px; overflow-y: auto; } +.save-status { color: var(--c-success); font-size: 0.85rem; margin: 0.5rem 0 0; } + +/* Languages */ +.language-grid { + list-style: none; padding: 0; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); + gap: 0.75rem; +} +.language-card { + background: #fff; + border: 1px solid var(--c-border); + border-radius: var(--radius); +} +.language-card a { + display: flex; flex-direction: column; + padding: 1rem 1.2rem; + color: var(--c-text); + text-decoration: none; +} +.language-card a:hover { background: var(--c-bg-alt); } +/* Right-to-left languages: the native name is RTL, but the English language name + and the English "N simulations" line must stay left-to-right. Applying + direction:rtl to the whole card reversed those too, rendering "simulations 103". */ +.language-card.rtl a { text-align: right; } +.language-card.rtl .language-native { direction: rtl; unicode-bidi: isolate; } +.language-card.rtl .language-name, +.language-card.rtl .language-count { direction: ltr; unicode-bidi: isolate; } +.language-name { font-weight: 700; font-size: 0.95rem; color: var(--phet-blue); } +.language-native { color: var(--c-muted); font-size: 0.85rem; } +.language-count { font-size: 0.78rem; color: var(--phet-magenta); margin-top: 0.25rem; font-weight: 700; } + +/* Teachers */ +.teachers-hero { + background: var(--c-bg-alt); + border-left: 5px solid var(--phet-orange); + padding: 1.75rem 2rem; + border-radius: var(--radius); + margin-bottom: 2rem; +} +.teachers-hero h1 { margin: 0 0 0.5rem; color: var(--phet-orange); font-weight: normal; } +.activity-grid { + list-style: none; padding: 0; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 1rem; +} +.activity-card { + background: #fff; + border: 1px solid var(--c-border); + border-top: 3px solid var(--phet-orange); + padding: 1.1rem 1.2rem; +} +.activity-card h3 { margin: 0 0 0.4rem; font-size: 1rem; } +.activity-card h3 a { color: var(--phet-blue); } +.activity-author { color: var(--c-muted); font-size: 0.82rem; margin-top: 0.5rem; } +.activity-detail .activity-section { margin: 1.5rem 0; } +.activity-detail h1 { margin: 0 0 0.5rem; color: var(--phet-orange); font-weight: normal; } + +/* Category */ +.category-header { + background: var(--c-bg-alt); + border-left: 5px solid var(--tile-color, var(--phet-blue)); + padding: 1.5rem; + border-radius: var(--radius); + margin-bottom: 1.5rem; +} +.category-header h1 { margin: 0 0 0.25rem; color: var(--phet-orange); font-weight: normal; } +.category-count { color: var(--c-muted); margin: 0.5rem 0 0; } + +/* About */ +.prose { max-width: 720px; } +.prose h2 { margin-top: 2rem; color: var(--phet-blue); } +.stats-grid { + list-style: none; padding: 0; + display: grid; + grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); + gap: 1rem; + margin: 1.5rem 0; +} +.stat-card { + background: var(--c-bg-alt); + padding: 1.2rem; + border-radius: var(--radius); + text-align: center; + border-top: 3px solid var(--phet-magenta); +} +.stat-card strong { display: block; font-size: 1.8rem; color: var(--phet-blue); } +.stat-card span { color: var(--c-muted); font-size: 0.85rem; } + +/* Account */ +.saved-list { list-style: none; padding: 0; } +.saved-item { + background: #fff; + border: 1px solid var(--c-border); + border-left: 3px solid var(--phet-magenta); + padding: 1rem 1.25rem; + margin-bottom: 0.75rem; +} +.saved-item h3 { margin: 0 0 0.25rem; font-size: 1.05rem; } +.saved-item h3 a { color: var(--phet-blue); } +.saved-desc { color: var(--c-muted); margin: 0 0 0.5rem; font-size: 0.9rem; } +.saved-notes { background: #fff8e1; padding: 0.5rem 0.75rem; border-radius: 4px; font-size: 0.88rem; } +.saved-meta { color: var(--c-muted); font-size: 0.8rem; margin: 0.4rem 0 0; } + +/* Auth */ +.auth-card { + max-width: 420px; margin: 2rem auto; + background: #fff; + border: 1px solid var(--c-border); + border-top: 4px solid var(--phet-magenta); + padding: 2rem; + box-shadow: 0 2px 8px rgba(0,0,0,0.05); +} +.auth-card h1 { margin: 0 0 0.5rem; font-size: 1.4rem; color: var(--phet-orange); font-weight: normal; } +.auth-sub { color: var(--c-muted); margin: 0 0 1.5rem; font-size: 0.9rem; } +.auth-card label { + display: block; margin-bottom: 0.85rem; + font-size: 0.78rem; color: var(--c-muted); + text-transform: uppercase; letter-spacing: 0.05em; +} +.auth-card input { + display: block; width: 100%; + margin-top: 0.2rem; + padding: 0.55rem 0.7rem; + border: 1px solid var(--c-border-strong); + border-radius: var(--radius); + font-size: 0.95rem; +} +.auth-foot { color: var(--c-muted); font-size: 0.88rem; margin-top: 1rem; text-align: center; } + +/* Error */ +.error-page { text-align: center; padding: 3rem 1rem; } +.error-page ul { list-style: none; padding: 0; } +.error-page li { display: inline-block; margin: 0 0.5rem; } + +/* === Footer (white top + grey sponsor band, real PhET look) === */ +.footer-top { + background: #fff; + padding: 2.5rem 1.25rem 1.5rem; + border-top: 1px solid var(--c-border); +} +.footer-social { + display: flex; justify-content: center; gap: 1.25rem; + margin-bottom: 1.25rem; +} +.footer-social a { + width: 22px; height: 22px; + display: flex; align-items: center; justify-content: center; + color: var(--c-text); +} +.footer-social a:hover { color: var(--phet-magenta); } +.footer-cols { + max-width: 720px; margin: 0 auto; + display: grid; grid-template-columns: repeat(4, 1fr); + gap: 1rem; text-align: center; +} +@media (max-width: 600px) { .footer-cols { grid-template-columns: repeat(2, 1fr); } } +.footer-cols ul { list-style: none; padding: 0; margin: 0; } +.footer-cols li { margin: 0.2rem 0; } +.footer-cols a { + color: var(--c-muted); + font-size: 0.72rem; + text-transform: uppercase; + letter-spacing: 0.04em; +} +.footer-cols a:hover { color: var(--phet-magenta); text-decoration: none; } + +.footer-lang { + text-align: center; margin: 1.5rem 0; +} +.footer-lang select { + padding: 0.35rem 1.5rem 0.35rem 0.6rem; + border: 1px solid var(--c-border-strong); + border-radius: 3px; + background: #fff; + font-size: 0.85rem; + color: var(--c-text); + min-width: 180px; +} +.footer-apps { + display: flex; justify-content: center; gap: 0.75rem; + margin: 1.25rem 0; + flex-wrap: wrap; +} +.footer-apps img { height: 36px; } +.footer-apps-link { + display: block; text-align: center; + font-size: 0.75rem; color: var(--c-text); + text-transform: uppercase; letter-spacing: 0.05em; + margin: 0.75rem 0; + font-weight: 700; +} +.footer-apps-link:hover { color: var(--phet-magenta); } + +/* Real sponsor logos strip (light grey) */ +.sponsor-strip { + background: #f0f0f0; + padding: 2rem 1.25rem; + margin: 0; + border-top: 1px solid var(--c-border); +} +.sponsor-inner { + max-width: var(--max-width); margin: 0 auto; + display: flex; align-items: center; justify-content: center; + flex-wrap: wrap; gap: 1.5rem 2.5rem; +} +.sponsor-inner img { + max-height: 50px; + width: auto; + filter: grayscale(100%); + opacity: 0.8; + transition: opacity 0.2s, filter 0.2s; +} +.sponsor-inner img:hover { filter: none; opacity: 1; } +.sponsor-phet-mark { + display: flex; align-items: center; gap: 0.75rem; + font-size: 0.75rem; color: var(--c-muted); + border-left: 1px solid var(--c-border-strong); + padding-left: 1.5rem; + margin-left: 1rem; +} +.sponsor-phet-mark img { height: 28px; filter: none; opacity: 1; } +.sponsor-phet-mark span { line-height: 1.3; } + +/* Final fineprint footer (very small bottom strip) */ +.footer-fineprint { + background: #f0f0f0; + border-top: 1px solid var(--c-border); + padding: 0.85rem 1.25rem; + color: var(--c-muted); + font-size: 0.72rem; + text-align: center; +} + +/* --------------------------------------------------------------------------- + Responsive header (reviewer fix) + The desktop nav, search form and user menu previously stayed on one row at + every width, which on its own forced the document to ~837px and produced a + horizontal overflow on every page below that. Below 900px the nav collapses + behind a toggle, mirroring the upstream site's mobile behaviour. + --------------------------------------------------------------------------- */ + +.nav-toggle { + display: none; + margin-left: auto; + background: transparent; + border: 0; + color: var(--c-text); + padding: 0.4rem; + border-radius: var(--radius); + cursor: pointer; + line-height: 0; +} +.nav-toggle:hover { color: var(--phet-magenta); } +.nav-toggle:focus-visible { outline: 2px solid var(--phet-magenta); outline-offset: 2px; } +.nav-toggle .nav-toggle-x { display: none; } +.nav-toggle[aria-expanded="true"] .nav-toggle-bars { display: none; } +.nav-toggle[aria-expanded="true"] .nav-toggle-x { display: inline; } + +@media (max-width: 900px) { + .topbar-inner { + flex-wrap: wrap; + gap: 0.5rem 0.75rem; + padding: 0.5rem 1rem; + } + .brand { margin-right: auto; } + .brand-phet { height: 34px; } + .brand-cu { height: 30px; } + .brand-divider { height: 28px; } + + .nav-toggle { display: inline-flex; order: 3; margin-left: 0; } + .search-form { order: 2; margin-left: 0; } + .user-menu { order: 1; } + + /* the nav becomes a stacked panel on its own row, hidden until toggled */ + .primary-nav { + order: 4; + display: none; + flex-direction: column; + align-items: stretch; + width: 100%; + margin-left: 0; + border-top: 1px solid var(--c-border); + padding-bottom: 0.4rem; + } + .primary-nav.is-open { display: flex; } + + .nav-item { display: block; } + .nav-item > a { + display: block; + padding: 0.7rem 0.25rem 0.35rem; + font-size: 0.82rem; + } + + /* sub-links are always visible inside the open panel: hover menus do not + work on touch, and the benchmark agent must be able to reach them */ + .nav-dropdown { + display: block; + position: static; + border: 0; + box-shadow: none; + min-width: 0; + padding: 0 0 0.35rem 0.9rem; + background: transparent; + } + .nav-dropdown a { padding: 0.4rem 0.25rem; font-size: 0.86rem; } + .nav-dropdown hr { margin: 0.3rem 0; } + + /* the search field is absolutely positioned to the left of its button on + desktop, which pushes it off-screen in a narrow bar; inline it instead */ + .search-form { position: static; flex: 1 1 auto; max-width: 260px; } + .search-form input[type=search] { + position: static; + transform: none; + visibility: visible; + width: 100%; + padding-left: 0.55rem; + padding-right: 0.55rem; + border-right: 1px solid var(--c-border-strong); + border-radius: var(--radius); + } + .search-form:hover input[type=search], + .search-form:focus-within input[type=search] { width: 100%; } +} + +@media (max-width: 520px) { + .search-form { order: 4; max-width: none; flex-basis: 100%; } + .primary-nav { order: 5; } + .brand-cu, .brand-divider { display: none; } +} + +/* reviewer fix: grid children default to min-width:auto, so a wide child kept + the simulation detail column from shrinking below ~456px on a phone. */ +.sim-detail-body > * { min-width: 0; } +.sim-detail-main, .sim-detail-side { min-width: 0; } +img { max-width: 100%; } + +/* reviewer fix: the detail tab strip did not wrap or scroll, so the last tab + overhung a phone viewport. Contain the overflow inside the strip itself. */ +.detail-tabs { overflow-x: auto; scrollbar-width: thin; -webkit-overflow-scrolling: touch; } +.detail-tab { white-space: nowrap; flex: 0 0 auto; } +@media (max-width: 520px) { .detail-tabs { gap: 1.25rem; } } + +/* Save-note field on the simulation detail side card (reviewer fix: the task + asks for a note and the API stores one, but the form had no way to enter it). */ +.save-note-label { + display: block; + font-size: 0.78rem; + font-weight: 600; + letter-spacing: 0.02em; + text-transform: uppercase; + color: var(--c-text-muted); + margin-bottom: 0.3rem; +} +.save-note { + width: 100%; + padding: 0.5rem 0.6rem; + margin-bottom: 0.6rem; + border: 1px solid var(--c-border-strong); + border-radius: var(--radius); + font: inherit; + font-size: 0.88rem; + background: #fff; + color: var(--c-text); +} +.save-note:focus-visible { outline: 2px solid var(--phet-magenta); outline-offset: 1px; } diff --git a/sites/phet_simulations/static/js/main.js b/sites/phet_simulations/static/js/main.js new file mode 100644 index 00000000..ce4a8d34 --- /dev/null +++ b/sites/phet_simulations/static/js/main.js @@ -0,0 +1,73 @@ +(function () { + function getCsrfToken(form) { + var input = form.querySelector('input[name="csrf_token"]'); + return input ? input.value : ''; + } + + document.querySelectorAll('.save-form').forEach(function (form) { + var status = form.querySelector('.save-status'); + form.querySelectorAll('button[data-action]').forEach(function (btn) { + btn.addEventListener('click', function () { + var action = btn.getAttribute('data-action'); + var simId = form.getAttribute('data-sim-id'); + var endpoint = action === 'save' ? '/api/save-sim' : '/api/unsave-sim'; + + fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': getCsrfToken(form), + }, + body: JSON.stringify( + action === 'save' + ? { sim_id: simId, notes: (form.querySelector('.save-note') || {}).value || '' } + : { sim_id: simId } + ), + }) + .then(function (r) { return r.json(); }) + .then(function (data) { + if (!data.ok) { + status.textContent = 'Error: ' + (data.error || 'unknown'); + status.style.color = '#c0392b'; + return; + } + if (action === 'save') { + btn.textContent = 'Remove from saved'; + btn.setAttribute('data-action', 'unsave'); + btn.classList.remove('btn-primary'); + btn.classList.add('btn-ghost'); + status.textContent = 'Saved to your account.'; + } else { + btn.textContent = 'Save to my account'; + btn.setAttribute('data-action', 'save'); + btn.classList.remove('btn-ghost'); + btn.classList.add('btn-primary'); + status.textContent = 'Removed from saved.'; + } + status.style.color = '#2e7d32'; + }) + .catch(function () { + status.textContent = 'Network error, please retry.'; + status.style.color = '#c0392b'; + }); + }); + }); + }); + + var navToggle = document.getElementById('nav-toggle'); + var primaryNav = document.getElementById('primary-nav'); + if (navToggle && primaryNav) { + navToggle.addEventListener('click', function () { + var open = primaryNav.classList.toggle('is-open'); + navToggle.setAttribute('aria-expanded', open ? 'true' : 'false'); + navToggle.setAttribute('aria-label', open ? 'Close menu' : 'Open menu'); + }); + } + + var playBtn = document.querySelector('.sim-detail-play'); + if (playBtn) { + playBtn.addEventListener('click', function () { + alert('Mirror snapshot — simulation playback is disabled in benchmark mode.'); + }); + } +})(); diff --git a/sites/phet_simulations/tasks.jsonl b/sites/phet_simulations/tasks.jsonl new file mode 100644 index 00000000..c44e8e74 --- /dev/null +++ b/sites/phet_simulations/tasks.jsonl @@ -0,0 +1,18 @@ +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--0", "ques": "Filter the PhET catalog to Biology simulations that are suitable for Elementary School, and list the titles you get.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_0.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have applied both the Biology subject facet and the Elementary School grade facet on the simulations catalog. The final answer MUST list every title the filtered page returns and MUST NOT add titles that the filter excludes. An empty answer is a FAIL."} +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--1", "ques": "Open the 'Build an Atom' simulation page and report its version number and how many languages it is translated into.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_1.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have opened the named simulation's own detail page; neither figure is shown on a listing page. The final answer MUST state the version string exactly as the page prints it and the number of languages the page reports. An empty answer is a FAIL."} +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--2", "ques": "Exactly one Biology simulation on PhET is not offered at university level. Find it and report which grade levels it does target.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_2.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have browsed the Biology listing and MUST have opened the detail page of the simulation it names, because grade levels appear only there. The final answer MUST name that one simulation and MUST list exactly the grade bands its page shows, without claiming university level. An empty answer is a FAIL."} +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--3", "ques": "Use the Release Type filter to show only simulations tagged New, then report how many of them were released during 2025.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_3.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have applied the New release filter. The final answer MUST report how many of those results carry a 2025 release date, which requires checking release dates rather than reporting the size of the whole New set. An empty answer is a FAIL."} +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--4", "ques": "Sort the PhET catalog by most translated and report the top simulation and its translation count.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_4.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have applied the most-translated sort on the catalog. The final answer MUST name the simulation that sorts first and MUST give its translation count as the site reports it. An empty answer is a FAIL."} +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--5", "ques": "Find the most recently released simulation in the PhET catalog and report its release date and which subjects it is filed under.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_5.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have used the catalog to establish which simulation is newest and MUST have opened that simulation's detail page. The final answer MUST give its release date and the subjects it is filed under. An empty answer is a FAIL."} +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--6", "ques": "Open the Translations page, find Arabic, and report how many simulations are available in that language.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_6.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have opened the Translations page. The final answer MUST report the simulation count the page lists for the requested language, not the catalog total and not the count for a regional variant. An empty answer is a FAIL."} +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--7", "ques": "On the Translations page, find the entry for Arabic (Morocco) and report the number of simulations listed for it.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_7.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have opened the Translations page and located the specific regional entry named in the task. The final answer MUST report that entry's simulation count, which differs from the count for the language without a region. An empty answer is a FAIL."} +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--8", "ques": "Search the PhET catalog for 'quantum'. Report how many results come back, then open the one that was released in 2026 and give its version number.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_8.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have run the site search for the given term and MUST have opened the detail page of the result released in 2026. The final answer MUST report how many results the search returned and the version string shown on that simulation's page. An empty answer is a FAIL."} +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--9", "ques": "Filter the catalog to the Heat & Thermo topic and report how many simulations it contains.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_9.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have applied the named topic facet on the catalog. The final answer MUST report the number of results that facet returns. An empty answer is a FAIL."} +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--10", "ques": "Open the 'Plinko Probability' simulation and list the related simulations recommended on its page.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_10.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have opened the named simulation's detail page. The final answer MUST list the simulations shown in that page's related-simulations section, all of them and no others. An empty answer is a FAIL."} +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--11", "ques": "Open the Customize tab of the PhET simulations catalog and report how many simulations are listed there.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_11.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have opened the Customize tab of the simulations catalog. The final answer MUST report how many simulations that tab lists, which is smaller than the full catalog. An empty answer is a FAIL."} +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--12", "ques": "Sign in to PhET as teacher@phet.test (password phet-teacher-pass) and report how many simulations are saved to that account.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_12.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have signed in with the account named in the task and MUST have opened the account page. The final answer MUST report how many simulations are saved to that account. The run MUST NOT change the saved list. An empty answer is a FAIL."} +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--13", "ques": "Sign in as student@phet.test (password phet-student-pass), open the 'Membrane Transport' simulation, and save it to your account with a note.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_13.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have signed in as the named student account, opened the named simulation's detail page, and saved it to the account with a note. Exactly one new saved row must exist for that account afterwards, on the named simulation, carrying a non-empty note. No other account's saved list may change."} +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--14", "ques": "Create a new teacher account on PhET with the email test_user@phet.test, then save the 'Number Pairs' simulation to that account.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_14.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have completed registration with the exact email given in the task and MUST have saved the named simulation to the new account. Exactly one new user row and exactly one new saved row must exist afterwards. An empty answer is a FAIL only if the task also asked for a reported value."} +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--15", "ques": "Open the teacher Activities list, filter it to Elementary School, and report the title and duration of the activity you find.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_15.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have opened the teacher activities list and applied the Elementary School grade filter. The final answer MUST give the title and the duration in minutes of the activity that filter returns. An empty answer is a FAIL."} +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--16", "ques": "On the PhET About page, report the total number of simulations, subject areas, languages and teacher activities listed.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_16.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have opened the About page. The final answer MUST report all four published totals: simulations, subject areas, languages and teacher activities. Missing any one of the four is a FAIL. An empty answer is a FAIL."} +{"web_name": "PhET Interactive Simulations", "id": "PhET Interactive Simulations--17", "ques": "Open 'Build an Atom' and 'Membrane Transport' and report which one is translated into more languages, and the difference between the two counts.", "web": "http://localhost:40029/", "upstream_url": "https://phet.colorado.edu/", "verifier_path": "sites/phet_simulations/verify/verify_17.py", "judge_rubric": "FACT CHECKPOINTS. The agent MUST have opened both named simulations' detail pages, because the translation count appears only there. The final answer MUST say which of the two is translated into more languages and MUST give the numeric difference between the two counts. An empty answer is a FAIL."} diff --git a/sites/phet_simulations/templates/404.html b/sites/phet_simulations/templates/404.html new file mode 100644 index 00000000..ac5e1718 --- /dev/null +++ b/sites/phet_simulations/templates/404.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} +{% block title %}Page not found · {{ site_title }}{% endblock %} +{% block content %} +
+

Page not found

+

The page you requested couldn't be found. Try one of these instead:

+ +
+{% endblock %} diff --git a/sites/phet_simulations/templates/_sim_card.html b/sites/phet_simulations/templates/_sim_card.html new file mode 100644 index 00000000..07db5856 --- /dev/null +++ b/sites/phet_simulations/templates/_sim_card.html @@ -0,0 +1,24 @@ + diff --git a/sites/phet_simulations/templates/about.html b/sites/phet_simulations/templates/about.html new file mode 100644 index 00000000..55f18438 --- /dev/null +++ b/sites/phet_simulations/templates/about.html @@ -0,0 +1,38 @@ +{% extends "base.html" %} +{% block title %}About · {{ site_title }}{% endblock %} +{% block content %} +

About PhET

+ +
+

Founded in 2002 by Nobel laureate Carl Wieman, the PhET Interactive + Simulations project at the University of Colorado Boulder creates + free interactive math and science simulations. PhET simulations are + based on extensive education research and engage students through an + intuitive, game-like environment where they learn through exploration + and discovery.

+ +

By the numbers

+
    +
  • {{ stats.simulations }}simulations
  • +
  • {{ stats.subjects }}subject areas
  • +
  • {{ stats.languages }}languages
  • +
  • {{ stats.activities }}teacher activities
  • +
+ +

Mission

+

To advance science and math literacy and education worldwide through + free interactive simulations.

+ +

License

+

PhET simulations are freely available under a Creative Commons + Attribution license (CC-BY). The HTML5 simulations are downloadable + for offline use, embeddable into other course materials, and + translatable into any language.

+ +

About this snapshot

+

This deployment is a static mirror of the PhET catalog created for + the WebHarbor benchmark. Simulation play buttons are disabled in + this snapshot; visit the live site at phet.colorado.edu to launch + simulations.

+
+{% endblock %} diff --git a/sites/phet_simulations/templates/accessibility.html b/sites/phet_simulations/templates/accessibility.html new file mode 100644 index 00000000..b4327b39 --- /dev/null +++ b/sites/phet_simulations/templates/accessibility.html @@ -0,0 +1,38 @@ +{% extends "base.html" %} +{% block title %}Accessibility · {{ site_title }}{% endblock %} +{% block content %} +

Accessibility

+ +
+

PhET is committed to making its simulations accessible to as many + learners as possible. Accessibility features include:

+ +

Keyboard navigation

+

All HTML5 simulations support full keyboard navigation, including + tab order, arrow-key adjustments, and shortcut keys for common + interactions. Tab focus is visually indicated with a high-contrast outline.

+ +

Screen reader support

+

Simulations include semantic markup, ARIA labels, and self-voicing + descriptions that announce state changes and user actions for users + of screen readers such as NVDA, JAWS, and VoiceOver.

+ +

Visual customization

+

Users can adjust contrast and color settings via browser-level + preferences. Simulations respect the operating system's reduced-motion + setting and avoid flashing content above the WCAG 2.1 threshold.

+ +

Sound and captions

+

Where audio is used, simulations provide optional captions and the + ability to mute sound entirely without losing instructional content.

+ +

Translations

+

Visit the Translations page + to find simulations in over twenty-five languages including + right-to-left scripts.

+ +

Feedback

+

To report an accessibility issue or suggest improvements, contact + the PhET team at phethelp@colorado.edu.

+
+{% endblock %} diff --git a/sites/phet_simulations/templates/account.html b/sites/phet_simulations/templates/account.html new file mode 100644 index 00000000..f308cd22 --- /dev/null +++ b/sites/phet_simulations/templates/account.html @@ -0,0 +1,36 @@ +{% extends "base.html" %} +{% block title %}My Account · {{ site_title }}{% endblock %} +{% block content %} +

Welcome, {{ current_user.name }}

+

+ {{ current_user.email }} + {% if current_user.institution %} · {{ current_user.institution }}{% endif %} + {% if current_user.country %} · {{ current_user.country }}{% endif %} + · Role: {{ current_user.role|capitalize }} +

+ +
+

Saved simulations ({{ saved_rows|length }})

+ {% if saved_rows %} +
    + {% for row in saved_rows %} +
  • +

    + {{ row.simulation.title }} +

    +

    {{ row.simulation.short_description }}

    + {% if row.notes %} +

    My notes: {{ row.notes }}

    + {% endif %} +

    Saved {{ row.saved_at.strftime('%B %d, %Y') }}

    +
  • + {% endfor %} +
+ {% else %} +

+ You haven't saved any simulations yet. + Browse simulations » +

+ {% endif %} +
+{% endblock %} diff --git a/sites/phet_simulations/templates/activities.html b/sites/phet_simulations/templates/activities.html new file mode 100644 index 00000000..7dd822ef --- /dev/null +++ b/sites/phet_simulations/templates/activities.html @@ -0,0 +1,45 @@ +{% extends "base.html" %} +{% block title %}Activities · {{ site_title }}{% endblock %} +{% block content %} + + +

Teacher-submitted activities

+

{{ activities|length }} activities published.

+ +
+ + + Reset +
+ +{% if activities %} +
    + {% for activity in activities %} +
  • +

    {{ activity.title }}

    +

    + For {{ activity.simulation.title }} · + {{ activity.grade_level|capitalize }} · + {{ activity.duration_min }} min +

    +

    {{ activity.description }}

    +

    By {{ activity.author }} · {{ activity.duration_min }} min · {{ activity.grade_level|title }}

    +
  • + {% endfor %} +
+{% else %} +

No activities for the selected filter.

+{% endif %} +{% endblock %} diff --git a/sites/phet_simulations/templates/activity_detail.html b/sites/phet_simulations/templates/activity_detail.html new file mode 100644 index 00000000..43520e82 --- /dev/null +++ b/sites/phet_simulations/templates/activity_detail.html @@ -0,0 +1,44 @@ +{% extends "base.html" %} +{% block title %}{{ activity.title }} · {{ site_title }}{% endblock %} +{% block content %} + + + +{% endblock %} diff --git a/sites/phet_simulations/templates/base.html b/sites/phet_simulations/templates/base.html new file mode 100644 index 00000000..a7d6b467 --- /dev/null +++ b/sites/phet_simulations/templates/base.html @@ -0,0 +1,219 @@ + + + + + + {% block title %}{{ site_title }}{% endblock %} + + + + +
+
+ + PhET + + University of Colorado Boulder + + + + + + + + +
+ {% if current_user.is_authenticated %} + + + + {% else %} + + + + {% endif %} +
+
+
+ +
+ Educators: To receive PhET's monthly newsletter, + register for a free educator account. + +
+ +{% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
+ {% for category, msg in messages %} +
{{ msg }}
+ {% endfor %} +
+ {% endif %} +{% endwith %} + +{% block hero %}{% endblock %} +{% block stats %}{% endblock %} +{% block full_bleed_top %}{% endblock %} + +
+ {% block content %}{% endblock %} +
+ +{% block full_bleed_bottom %}{% endblock %} + + + + + + + + + + diff --git a/sites/phet_simulations/templates/category.html b/sites/phet_simulations/templates/category.html new file mode 100644 index 00000000..6a590e6d --- /dev/null +++ b/sites/phet_simulations/templates/category.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} +{% block title %}{{ subject.name }} Simulations · {{ site_title }}{% endblock %} +{% block content %} +
+

{{ subject.name }}

+

{{ subject.description }}

+

{{ sims|length }} simulation{{ '' if sims|length == 1 else 's' }} in this category.

+
+ +{% if sims %} +
+ {% for sim in sims %} + {% include "_sim_card.html" %} + {% endfor %} +
+{% else %} +

No simulations available in this category yet.

+{% endif %} +{% endblock %} diff --git a/sites/phet_simulations/templates/index.html b/sites/phet_simulations/templates/index.html new file mode 100644 index 00000000..a85882a0 --- /dev/null +++ b/sites/phet_simulations/templates/index.html @@ -0,0 +1,151 @@ +{% extends "base.html" %} +{% set subjects_ordered = [ + ('physics', 'Physics'), + ('math', 'Math & Statistics'), + ('chemistry', 'Chemistry'), + ('earth-science', 'Earth & Space'), + ('biology', 'Biology'), +] %} +{% set subj_icon = { + 'physics': 'physics', + 'math': 'math-and-statistics', + 'chemistry': 'chemistry', + 'earth-science': 'earth-and-space', + 'biology': 'biology', +} %} + +{% block hero %} +
+
+
+

Interactive Simulations
for Science and Math

+ Explore our sims +
+
+
+{% endblock %} + +{% block stats %} +
+ Over 1.8 billion simulations delivered +
+{% endblock %} + +{% block main_class %}container home-main{% endblock %} + +{% block content %} + +
+ {% for slug, name in subjects_ordered %} + + + {{ name }} + + {% endfor %} +
+ +{% if featured %} +
+
+

Featured Simulations

+ View all » +
+
+ {% for sim in featured %}{% include "_sim_card.html" %}{% endfor %} +
+
+{% endif %} + +{% if new_sims %} +
+
+

New Sims

+ View all » +
+
+ {% for sim in new_sims %}{% include "_sim_card.html" %}{% endfor %} +
+
+{% endif %} + +{% if most_played %} +
+
+

Recently Updated

+ View all » +
+
+ {% for sim in most_played %}{% include "_sim_card.html" %}{% endfor %} +
+
+{% endif %} +{% endblock %} + +{% block full_bleed_top %}{% endblock %} + +{% block full_bleed_bottom %} +
+
+
+

Teaching Resources,
Activities, and Community

+

Teachers have access to simulations specific to grade and video content, + resources for teaching with simulations, and activities created by our + teacher community.

+ Register now +
+
+
+ +
+
+ Interact. Discover. Learn. +
+
+
+ {{ total_simulations }} + interactive simulations +
+
+
+ {{ total_languages }} + language translations +
+
+
+ {{ total_activities }} + teacher-submitted lessons +
+
+
+

+ Founded in 2002 by Nobel Laureate Carl Wieman, the PhET Interactive Simulations + project at the University of Colorado Boulder creates free interactive math and + science simulations. PhET sims are based on extensive + research + and engage students through an intuitive, game-like environment where students + learn through exploration and discovery. +

+
+ +
+
+
+

Shouldn't All Students
Experience STEM?

+

Learn how we are tackling challenges in STEM education, software development, + and assistive technology.

+ Explore accessible sims +
+
+
+ +
+
PhET is supported in part by
+ +

+ and other supporters, including educators like you. +

+ +
+{% endblock %} diff --git a/sites/phet_simulations/templates/login.html b/sites/phet_simulations/templates/login.html new file mode 100644 index 00000000..f4ff3a51 --- /dev/null +++ b/sites/phet_simulations/templates/login.html @@ -0,0 +1,25 @@ +{% extends "base.html" %} +{% block title %}Sign In · {{ site_title }}{% endblock %} +{% block content %} +
+

Sign in to PhET

+

Sign in to save simulations, write notes, and access teacher resources.

+ +
+ + + + +
+ +

+ Don't have an account? Create one now. +

+
+{% endblock %} diff --git a/sites/phet_simulations/templates/register.html b/sites/phet_simulations/templates/register.html new file mode 100644 index 00000000..81072614 --- /dev/null +++ b/sites/phet_simulations/templates/register.html @@ -0,0 +1,37 @@ +{% extends "base.html" %} +{% block title %}Create Account · {{ site_title }}{% endblock %} +{% block content %} +
+

Create your PhET account

+

A free account lets you save simulations and download teacher resources.

+ +
+ + + + + + + +
+ +

+ Already have an account? Sign in. +

+
+{% endblock %} diff --git a/sites/phet_simulations/templates/search.html b/sites/phet_simulations/templates/search.html new file mode 100644 index 00000000..95d69c4a --- /dev/null +++ b/sites/phet_simulations/templates/search.html @@ -0,0 +1,25 @@ +{% extends "base.html" %} +{% block title %}{% if query %}Search: {{ query }}{% else %}Search{% endif %} · {{ site_title }}{% endblock %} +{% block content %} +

Search

+ + + +{% if query %} +

{{ total }} result{{ '' if total == 1 else 's' }} for “{{ query }}”

+ {% if sims %} +
+ {% for sim in sims %} + {% include "_sim_card.html" %} + {% endfor %} +
+ {% else %} +

No simulations matched your search. Try a shorter or more general term.

+ {% endif %} +{% else %} +

Enter a search term to find simulations by title or topic.

+{% endif %} +{% endblock %} diff --git a/sites/phet_simulations/templates/simulation_detail.html b/sites/phet_simulations/templates/simulation_detail.html new file mode 100644 index 00000000..80932009 --- /dev/null +++ b/sites/phet_simulations/templates/simulation_detail.html @@ -0,0 +1,188 @@ +{% extends "base.html" %} +{% block title %}{{ sim.title }} · {{ site_title }}{% endblock %} +{% block content %} + + +
+
+
+ +
+ {% if sim.slug in available_thumbnails %} + {{ sim.title }} screenshot + {% else %} +
{{ sim.title }}
+ {% endif %} + +
+ +
+

{{ sim.title }}

+
    + {% if sim.is_html5 %}
  • HTML5
  • {% endif %} + {% if sim.is_new %}
  • New
  • {% endif %} + {% if sim.is_featured %}{% endif %} +
  • v{{ sim.version }}
  • +
+ +
+ + + +
+

About this simulation

+

{{ sim.overview }}

+

{{ sim.short_description }}

+
+ +
+

Topics

+
    + {% for topic in sim.topics() %}
  • {{ topic|capitalize }}
  • {% endfor %} +
+
+ +
+

Sample Learning Goals

+
    +
  • Use the {{ sim.title.lower() }} simulation to model the underlying concepts.
  • +
  • Identify the variables and determine the relationships among them.
  • +
  • Predict how changes to one parameter affect the rest of the system.
  • +
+
+ + {% if activities %} +
+

Teaching Resources — submitted activities ({{ activities|length }})

+
    + {% for activity in activities %} +
  • + {{ activity.title }} + by {{ activity.author }} · {{ activity.grade_level|capitalize }} · {{ activity.duration_min }} min +
  • + {% endfor %} +
+
+ {% endif %} + +
+

Translations ({{ languages|length }})

+
+ {% for l in languages %} + {{ l.name }} + {% endfor %} +
+
+ +
+

System Requirements & Credits

+

HTML5 simulations can run on Chromebooks, iPads, iPhones, PCs, Mac, and Linux systems. + Inclusive features added to HTML5 sims only; some features have platform limitations.

+

Version: {{ sim.version }} · Released {{ sim.release_date }}

+
+ + {% if related %} +
+

Related Sims

+
+ {% for sim in related %} + {% include "_sim_card.html" %} + {% endfor %} +
+
+ {% endif %} +
+ + +
+ +
+

Explore More

+ +

+ PhET is a non-profit committed to providing high quality STEM resources for every classroom. +

+ +
+
+{% endblock %} diff --git a/sites/phet_simulations/templates/simulations.html b/sites/phet_simulations/templates/simulations.html new file mode 100644 index 00000000..7d12fe51 --- /dev/null +++ b/sites/phet_simulations/templates/simulations.html @@ -0,0 +1,230 @@ +{% extends "base.html" %} +{% block title %}Simulations · {{ site_title }}{% endblock %} + +{% block hero %} +
+

Simulations

+
+{% endblock %} + +{% block stats %} + +{% endblock %} + +{% block main_class %}{% endblock %} + +{% block content %} + +{% if view_tab == 'filter' %} + {# === Filter mode: left sidebar + filtered results === #} +
+ + +
+
+ {{ total }} result{{ '' if total == 1 else 's' }} +
+ {% if subject %} + + {{ subject|upper }} + × + + {% endif %} + {% if grade %} + + {{ grade|upper }} + × + + {% endif %} + {% if language %} + + {{ language|upper }} + × + + {% endif %} + {% if release %} + + {{ 'NEW' if release == 'new' else 'RECENTLY UPDATED' }} + × + + {% endif %} +
+
+ + +
+
+ + {% if sims %} +
+ {% for sim in sims %}{% include "_sim_card.html" %}{% endfor %} +
+ {% if total_pages > 1 %} + + {% endif %} + {% else %} +

No simulations match the selected filters.

+ {% endif %} +
+
+ +{% elif view_tab == 'customize' %} + {# === Customize mode: simulations that ship a teacher's guide upstream === #} +
+
+
+

Customizable Sims

+ {{ customizable | length }} available in PhET Studio +
+ {% if customizable %} +
+ {% for sim in customizable %} + {% include "_sim_card.html" %} + {% endfor %} +
+ {% else %} +

No customizable simulations in this snapshot.

+ {% endif %} +
+
+ +{% else %} + {# === Browse mode: grouped by subject (real PhET default) === #} +
+ {% for slug, name in [('physics', 'Physics'), ('math', 'Math & Statistics'), + ('chemistry', 'Chemistry'), ('earth-science', 'Earth & Space'), + ('biology', 'Biology')] %} + {% set bucket = sims_by_subject.get(slug, []) %} + {% if bucket %} +
+
+

{{ name }}

+ View all » +
+
+ {% for sim in bucket %} + {% include "_sim_card.html" %} + {% endfor %} +
+
+ {% endif %} + {% endfor %} +
+{% endif %} + +{% endblock %} diff --git a/sites/phet_simulations/templates/teachers.html b/sites/phet_simulations/templates/teachers.html new file mode 100644 index 00000000..8f925606 --- /dev/null +++ b/sites/phet_simulations/templates/teachers.html @@ -0,0 +1,32 @@ +{% extends "base.html" %} +{% block title %}For Teachers · {{ site_title }}{% endblock %} +{% block content %} +
+

Resources for teachers

+

Lesson plans, activities, and tips for using PhET simulations in your classroom. + Activities are contributed by educators and aligned to common curriculum standards.

+

+ Browse all activities + Find a simulation +

+
+ +
+

Most-downloaded activities

+
    + {% for activity in featured_activities %} +
  • +

    {{ activity.title }}

    +

    + For {{ activity.simulation.title }} · + {{ activity.grade_level|capitalize }} · + {{ activity.duration_min }} min · + {{ activity.file_type }} +

    +

    {{ activity.description }}

    +

    By {{ activity.author }} · {{ activity.duration_min }} min

    +
  • + {% endfor %} +
+
+{% endblock %} diff --git a/sites/phet_simulations/templates/translation_detail.html b/sites/phet_simulations/templates/translation_detail.html new file mode 100644 index 00000000..59aacfe4 --- /dev/null +++ b/sites/phet_simulations/templates/translation_detail.html @@ -0,0 +1,25 @@ +{% extends "base.html" %} +{% block title %}{{ language.name }} translations · {{ site_title }}{% endblock %} +{% block content %} + + +

{{ language.name }} ({{ language.native_name }})

+

+ {{ sims|length }} simulations available in {{ language.name }}. + Language code: {{ language.code }}{% if language.is_rtl %} · right-to-left script{% endif %}. +

+ +{% if sims %} +
+ {% for sim in sims %} + {% include "_sim_card.html" %} + {% endfor %} +
+{% else %} +

No simulations are translated to this language yet.

+{% endif %} +{% endblock %} diff --git a/sites/phet_simulations/templates/translations.html b/sites/phet_simulations/templates/translations.html new file mode 100644 index 00000000..889467ce --- /dev/null +++ b/sites/phet_simulations/templates/translations.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} +{% block title %}Translations · {{ site_title }}{% endblock %} +{% block content %} +

Translations

+

+ PhET simulations are translated into {{ languages|length }} languages by educators and volunteers worldwide. + Select a language to see which simulations are available. +

+ + +{% endblock %} diff --git a/sites/phet_simulations/verify/tests/adversarial_matrix.py b/sites/phet_simulations/verify/tests/adversarial_matrix.py new file mode 100644 index 00000000..e92cd2fb --- /dev/null +++ b/sites/phet_simulations/verify/tests/adversarial_matrix.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +"""Adversarial matrix for the PhET grading contract. + +Builds fixtures from the canonical runs and asserts each verifier's verdict. +Fixtures are constructed test inputs, NOT agent runs: they never count as +trajectories and are written to a separate directory. + +Cells per task: + genuine the canonical run -> PASS + noop homepage only, empty answer, unchanged DB -> FAIL + wrong canonical trajectory, answer corrupted -> FAIL + shortcut correct answer, navigation stripped -> FAIL + dirty read-only task, extra row written to after.db -> FAIL + state_missing stateful task, after.db reset to initial -> FAIL +""" +import json, shutil, sqlite3, subprocess, sys, pathlib, re + +CASE = pathlib.Path(__file__).resolve().parent.parent +W = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else + "/Users/jinzexu/Documents/ChatGPT/webharbor/WHR-029-phet-simulations") +RUNS = CASE / "runs/canonical-2026-09-13" +OUT = CASE / "evidence/adversarial-canonical-2026-09-14" +FIX = OUT / "fixtures" +STATEFUL = {13, 14} + +def verdict(n, run_dir, initial, after): + r = subprocess.run( + ["python3", str(W / "sites/phet_simulations/verify" / f"verify_{n}.py"), + "--run_dir", str(run_dir), "--initial_db", str(initial), + "--after_db", str(after), "--no_llm", "True"], + capture_output=True, text=True, cwd=str(W)) + try: + j = json.loads(r.stdout) + return ("PASS" if j["pass"] else "FAIL"), j.get("reason", "") + except Exception: + return "ERROR", (r.stdout + r.stderr)[:160] + +def clone(n, cell): + d = FIX / f"task_{n}_{cell}" + if d.exists(): shutil.rmtree(d) + shutil.copytree(RUNS / f"task_{n}", d) + return d + +def main(): + FIX.mkdir(parents=True, exist_ok=True) + rows = [] + for n in range(18): + src = RUNS / f"task_{n}" + base_i, base_a = src / "initial.db", src / "after.db" + + v, why = verdict(n, src, base_i, base_a) + rows.append({"task": n, "cell": "genuine", "expected": "PASS", "verdict": v, + "match": v == "PASS", "reason": why}) + + d = clone(n, "noop") + t = json.loads((d / "trajectory.json").read_text()) + t["steps"] = [s for s in t["steps"] if False] + t["final_answer"] = "" + t["success_self_report"] = False + (d / "trajectory.json").write_text(json.dumps(t, indent=2)) + shutil.copy(base_i, d / "after.db") + v, why = verdict(n, d, base_i, d / "after.db") + rows.append({"task": n, "cell": "noop", "expected": "FAIL", "verdict": v, + "match": v == "FAIL", "reason": why}) + + # Replace the answer wholesale rather than perturbing digits. Shifting the + # numbers inside an answer can leave every fact the task actually asks for + # intact (task 2 only had incidental age ranges to corrupt), which tests + # nothing. + d = clone(n, "wrong") + t = json.loads((d / "trajectory.json").read_text()) + t["final_answer"] = "Quantum Coin Toss, version 9.9.9, translated into 3 languages." + (d / "trajectory.json").write_text(json.dumps(t, indent=2)) + v, why = verdict(n, d, base_i, base_a) + rows.append({"task": n, "cell": "wrong", "expected": "FAIL", "verdict": v, + "match": v == "FAIL", "reason": why}) + + d = clone(n, "shortcut") + t = json.loads((d / "trajectory.json").read_text()) + t["steps"] = [] + (d / "trajectory.json").write_text(json.dumps(t, indent=2)) + v, why = verdict(n, d, base_i, base_a) + rows.append({"task": n, "cell": "shortcut", "expected": "FAIL", "verdict": v, + "match": v == "FAIL", "reason": why}) + + if n not in STATEFUL: + d = clone(n, "dirty") + shutil.copy(base_a, d / "after.db") + con = sqlite3.connect(d / "after.db") + con.execute("INSERT INTO saved_simulation (user_id, sim_id, notes, saved_at) " + "VALUES (1, 5, 'adversarial fixture', '2026-01-01 00:00:00')") + con.commit(); con.close() + v, why = verdict(n, d, base_i, d / "after.db") + rows.append({"task": n, "cell": "dirty", "expected": "FAIL", "verdict": v, + "match": v == "FAIL", "reason": why}) + else: + d = clone(n, "state_missing") + shutil.copy(base_i, d / "after.db") + v, why = verdict(n, d, base_i, d / "after.db") + rows.append({"task": n, "cell": "state_missing", "expected": "FAIL", "verdict": v, + "match": v == "FAIL", "reason": why}) + + OUT.mkdir(parents=True, exist_ok=True) + (OUT / "results.json").write_text(json.dumps(rows, indent=1)) + ok = sum(1 for r in rows if r["match"]) + print(f"{ok}/{len(rows)} cells match expectation") + for r in rows: + if not r["match"]: + print(f" MISMATCH task {r['task']} {r['cell']}: expected {r['expected']}, got {r['verdict']} ({r['reason']})") + return 0 if ok == len(rows) else 1 + +sys.exit(main()) diff --git a/sites/phet_simulations/verify/verify_0 2.py b/sites/phet_simulations/verify/verify_0 2.py new file mode 100644 index 00000000..a11d60cd --- /dev/null +++ b/sites/phet_simulations/verify/verify_0 2.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--0. + +Filter to Biology + Elementary School and list the titles. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--0', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("used_biology_filter", navigated_to(t, "subject=biology"), + f"urls={[u for u in __import__('verify_lib').step_urls(t) if 'simulations' in u][:6]}") + j.check("used_grade_filter", navigated_to(t, "grade=elementary"), "grade=elementary in a visited URL") + j.check("answer_lists_all_three", contains_all(fa, ["Color Vision", "Density", "Natural Selection"]), + f"final={fa!r}") + j.check("answer_excludes_non_matches", + not contains_any(fa, ["Neuron", "Membrane Transport", "Gene Expression", "Molecule Polarity", "pH Scale"]), + f"final={fa!r}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_0.py b/sites/phet_simulations/verify/verify_0.py new file mode 100644 index 00000000..a11d60cd --- /dev/null +++ b/sites/phet_simulations/verify/verify_0.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--0. + +Filter to Biology + Elementary School and list the titles. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--0', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("used_biology_filter", navigated_to(t, "subject=biology"), + f"urls={[u for u in __import__('verify_lib').step_urls(t) if 'simulations' in u][:6]}") + j.check("used_grade_filter", navigated_to(t, "grade=elementary"), "grade=elementary in a visited URL") + j.check("answer_lists_all_three", contains_all(fa, ["Color Vision", "Density", "Natural Selection"]), + f"final={fa!r}") + j.check("answer_excludes_non_matches", + not contains_any(fa, ["Neuron", "Membrane Transport", "Gene Expression", "Molecule Polarity", "pH Scale"]), + f"final={fa!r}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_1 2.py b/sites/phet_simulations/verify/verify_1 2.py new file mode 100644 index 00000000..fb461fc0 --- /dev/null +++ b/sites/phet_simulations/verify/verify_1 2.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--1. + +Build an Atom: report version and translation count. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--1', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_build_an_atom", navigated_to(t, "/simulation/build-an-atom"), "detail page opened") + j.check("answer_version", contains_all(fa, ["1.9.3"]), f"final={fa!r}") + j.check("answer_translation_count", has_number(fa, 104), f"numbers={numbers_in(fa)}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_1.py b/sites/phet_simulations/verify/verify_1.py new file mode 100644 index 00000000..fb461fc0 --- /dev/null +++ b/sites/phet_simulations/verify/verify_1.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--1. + +Build an Atom: report version and translation count. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--1', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_build_an_atom", navigated_to(t, "/simulation/build-an-atom"), "detail page opened") + j.check("answer_version", contains_all(fa, ["1.9.3"]), f"final={fa!r}") + j.check("answer_translation_count", has_number(fa, 104), f"numbers={numbers_in(fa)}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_10 2.py b/sites/phet_simulations/verify/verify_10 2.py new file mode 100644 index 00000000..e078b8bc --- /dev/null +++ b/sites/phet_simulations/verify/verify_10 2.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--10. + +Plinko Probability: list the related simulations shown. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--10', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_plinko", navigated_to(t, "/simulation/plinko-probability"), "detail page opened") + j.check("answer_lists_related", contains_all(fa, ["Least-Squares Regression", "Projectile Data Lab", + "Projectile Sampling Distributions", + "Quantum Measurement", "Quantum Coin Toss"]), + f"final={fa!r}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_10.py b/sites/phet_simulations/verify/verify_10.py new file mode 100644 index 00000000..e078b8bc --- /dev/null +++ b/sites/phet_simulations/verify/verify_10.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--10. + +Plinko Probability: list the related simulations shown. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--10', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_plinko", navigated_to(t, "/simulation/plinko-probability"), "detail page opened") + j.check("answer_lists_related", contains_all(fa, ["Least-Squares Regression", "Projectile Data Lab", + "Projectile Sampling Distributions", + "Quantum Measurement", "Quantum Coin Toss"]), + f"final={fa!r}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_11 2.py b/sites/phet_simulations/verify/verify_11 2.py new file mode 100644 index 00000000..4f43d740 --- /dev/null +++ b/sites/phet_simulations/verify/verify_11 2.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--11. + +Customize tab: how many simulations are listed. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--11', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_customize", navigated_to(t, "view=customize"), "customize tab opened") + j.check("answer_count", has_number(fa, 49), f"numbers={numbers_in(fa)}") + j.check("answer_not_catalog_total", not has_number(fa, 120), "120 is the full catalogue") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_11.py b/sites/phet_simulations/verify/verify_11.py new file mode 100644 index 00000000..4f43d740 --- /dev/null +++ b/sites/phet_simulations/verify/verify_11.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--11. + +Customize tab: how many simulations are listed. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--11', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_customize", navigated_to(t, "view=customize"), "customize tab opened") + j.check("answer_count", has_number(fa, 49), f"numbers={numbers_in(fa)}") + j.check("answer_not_catalog_total", not has_number(fa, 120), "120 is the full catalogue") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_12 2.py b/sites/phet_simulations/verify/verify_12 2.py new file mode 100644 index 00000000..8384db16 --- /dev/null +++ b/sites/phet_simulations/verify/verify_12 2.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--12. + +Sign in as the teacher account and report how many sims are saved. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, counts, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--12', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_login", navigated_to(t, "/login"), "sign-in page visited") + j.check("nav_account", navigated_to(t, "/account"), "account page visited") + j.check("answer_count", counts(fa, 4, "simulation", "sim", "saved", "item"), + f"the count must be reported as a number of saved simulations; numbers={numbers_in(fa)} final={fa!r}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + saved = saved_sims_for(after, "teacher@phet.test") + j.check("account_still_has_four", saved is not None and len(saved) == 4, f"saved={saved}") + j.check("catalog_unchanged", catalog_unchanged(init, after) is True, "catalogue tables untouched") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_12.py b/sites/phet_simulations/verify/verify_12.py new file mode 100644 index 00000000..8384db16 --- /dev/null +++ b/sites/phet_simulations/verify/verify_12.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--12. + +Sign in as the teacher account and report how many sims are saved. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, counts, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--12', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_login", navigated_to(t, "/login"), "sign-in page visited") + j.check("nav_account", navigated_to(t, "/account"), "account page visited") + j.check("answer_count", counts(fa, 4, "simulation", "sim", "saved", "item"), + f"the count must be reported as a number of saved simulations; numbers={numbers_in(fa)} final={fa!r}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + saved = saved_sims_for(after, "teacher@phet.test") + j.check("account_still_has_four", saved is not None and len(saved) == 4, f"saved={saved}") + j.check("catalog_unchanged", catalog_unchanged(init, after) is True, "catalogue tables untouched") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_13 2.py b/sites/phet_simulations/verify/verify_13 2.py new file mode 100644 index 00000000..7522656b --- /dev/null +++ b/sites/phet_simulations/verify/verify_13 2.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--13. + +Student account saves Membrane Transport with a note. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--13', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("answer_names_saved_sim", contains_any(fa, ["Membrane Transport"]), + f"the final answer must say which simulation was saved; final={fa!r}") + j.check("nav_login", navigated_to(t, "/login"), "sign-in page visited") + j.check("nav_membrane_transport", navigated_to(t, "/simulation/membrane-transport"), "detail page opened") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + before = saved_sims_for(init, "student@phet.test") + rows = saved_rows_for(after, "student@phet.test") + j.check("exactly_one_new_save", + before is not None and rows is not None and len(rows) == len(before) + 1, + f"before={before} after={rows}") + j.check("saved_the_named_sim", + rows is not None and any(r[0] == "membrane-transport" for r in rows), f"after={rows}") + j.check("note_is_present", + rows is not None and any(r[0] == "membrane-transport" and r[1].strip() for r in rows), + f"after={rows}") + teacher = saved_sims_for(after, "teacher@phet.test") + j.check("other_account_untouched", teacher is not None and len(teacher) == 4, f"teacher={teacher}") + j.check("catalog_unchanged", catalog_unchanged(init, after) is True, "catalogue tables untouched") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_13.py b/sites/phet_simulations/verify/verify_13.py new file mode 100644 index 00000000..7522656b --- /dev/null +++ b/sites/phet_simulations/verify/verify_13.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--13. + +Student account saves Membrane Transport with a note. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--13', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("answer_names_saved_sim", contains_any(fa, ["Membrane Transport"]), + f"the final answer must say which simulation was saved; final={fa!r}") + j.check("nav_login", navigated_to(t, "/login"), "sign-in page visited") + j.check("nav_membrane_transport", navigated_to(t, "/simulation/membrane-transport"), "detail page opened") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + before = saved_sims_for(init, "student@phet.test") + rows = saved_rows_for(after, "student@phet.test") + j.check("exactly_one_new_save", + before is not None and rows is not None and len(rows) == len(before) + 1, + f"before={before} after={rows}") + j.check("saved_the_named_sim", + rows is not None and any(r[0] == "membrane-transport" for r in rows), f"after={rows}") + j.check("note_is_present", + rows is not None and any(r[0] == "membrane-transport" and r[1].strip() for r in rows), + f"after={rows}") + teacher = saved_sims_for(after, "teacher@phet.test") + j.check("other_account_untouched", teacher is not None and len(teacher) == 4, f"teacher={teacher}") + j.check("catalog_unchanged", catalog_unchanged(init, after) is True, "catalogue tables untouched") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_14 2.py b/sites/phet_simulations/verify/verify_14 2.py new file mode 100644 index 00000000..09402e76 --- /dev/null +++ b/sites/phet_simulations/verify/verify_14 2.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--14. + +Register test_user@phet.test and save Number Pairs to it. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--14', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("answer_names_saved_sim", contains_any(fa, ["Number Pairs"]), + f"the final answer must say which simulation was saved; final={fa!r}") + j.check("nav_register", navigated_to(t, "/register"), "registration page visited") + j.check("nav_number_pairs", navigated_to(t, "/simulation/number-pairs"), "detail page opened") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ci, ca = table_counts(init), table_counts(after) + j.check("exactly_one_new_user", + ci is not None and ca is not None and ca["user"] == ci["user"] + 1, + f"users {ci and ci['user']} -> {ca and ca['user']}") + j.check("new_user_has_the_named_email", user_exists(after, email="test_user@phet.test") is True, + "test_user@phet.test present") + rows = saved_rows_for(after, "test_user@phet.test") + j.check("saved_number_pairs", rows is not None and any(r[0] == "number-pairs" for r in rows), + f"saved={rows}") + j.check("exactly_one_new_save", + ci is not None and ca is not None and ca["saved_simulation"] == ci["saved_simulation"] + 1, + f"saved rows {ci and ci['saved_simulation']} -> {ca and ca['saved_simulation']}") + j.check("catalog_unchanged", catalog_unchanged(init, after) is True, "catalogue tables untouched") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_14.py b/sites/phet_simulations/verify/verify_14.py new file mode 100644 index 00000000..09402e76 --- /dev/null +++ b/sites/phet_simulations/verify/verify_14.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--14. + +Register test_user@phet.test and save Number Pairs to it. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--14', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("answer_names_saved_sim", contains_any(fa, ["Number Pairs"]), + f"the final answer must say which simulation was saved; final={fa!r}") + j.check("nav_register", navigated_to(t, "/register"), "registration page visited") + j.check("nav_number_pairs", navigated_to(t, "/simulation/number-pairs"), "detail page opened") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ci, ca = table_counts(init), table_counts(after) + j.check("exactly_one_new_user", + ci is not None and ca is not None and ca["user"] == ci["user"] + 1, + f"users {ci and ci['user']} -> {ca and ca['user']}") + j.check("new_user_has_the_named_email", user_exists(after, email="test_user@phet.test") is True, + "test_user@phet.test present") + rows = saved_rows_for(after, "test_user@phet.test") + j.check("saved_number_pairs", rows is not None and any(r[0] == "number-pairs" for r in rows), + f"saved={rows}") + j.check("exactly_one_new_save", + ci is not None and ca is not None and ca["saved_simulation"] == ci["saved_simulation"] + 1, + f"saved rows {ci and ci['saved_simulation']} -> {ca and ca['saved_simulation']}") + j.check("catalog_unchanged", catalog_unchanged(init, after) is True, "catalogue tables untouched") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_15 2.py b/sites/phet_simulations/verify/verify_15 2.py new file mode 100644 index 00000000..061710a2 --- /dev/null +++ b/sites/phet_simulations/verify/verify_15 2.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--15. + +Activities filtered to Elementary School: title and duration. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--15', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_activities", navigated_to(t, "/teachers/activities"), "activities list visited") + j.check("used_grade_filter", navigated_to(t, "grade=elementary"), "elementary filter applied") + j.check("answer_title", contains_any(fa, ["Equivalent Fractions Game"]), f"final={fa!r}") + j.check("answer_duration", has_number(fa, 50), f"numbers={numbers_in(fa)}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_15.py b/sites/phet_simulations/verify/verify_15.py new file mode 100644 index 00000000..061710a2 --- /dev/null +++ b/sites/phet_simulations/verify/verify_15.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--15. + +Activities filtered to Elementary School: title and duration. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--15', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_activities", navigated_to(t, "/teachers/activities"), "activities list visited") + j.check("used_grade_filter", navigated_to(t, "grade=elementary"), "elementary filter applied") + j.check("answer_title", contains_any(fa, ["Equivalent Fractions Game"]), f"final={fa!r}") + j.check("answer_duration", has_number(fa, 50), f"numbers={numbers_in(fa)}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_16 2.py b/sites/phet_simulations/verify/verify_16 2.py new file mode 100644 index 00000000..bae50629 --- /dev/null +++ b/sites/phet_simulations/verify/verify_16 2.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--16. + +About page: the four published totals. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--16', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_about", navigated_to(t, "/about"), "about page visited") + nums = numbers_in(fa) + j.check("answer_simulations", 120 in nums, f"numbers={nums}") + j.check("answer_subjects", 5 in nums, f"numbers={nums}") + j.check("answer_languages", 132 in nums, f"numbers={nums}") + j.check("answer_activities", 14 in nums, f"numbers={nums}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_16.py b/sites/phet_simulations/verify/verify_16.py new file mode 100644 index 00000000..bae50629 --- /dev/null +++ b/sites/phet_simulations/verify/verify_16.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--16. + +About page: the four published totals. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--16', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_about", navigated_to(t, "/about"), "about page visited") + nums = numbers_in(fa) + j.check("answer_simulations", 120 in nums, f"numbers={nums}") + j.check("answer_subjects", 5 in nums, f"numbers={nums}") + j.check("answer_languages", 132 in nums, f"numbers={nums}") + j.check("answer_activities", 14 in nums, f"numbers={nums}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_17 2.py b/sites/phet_simulations/verify/verify_17 2.py new file mode 100644 index 00000000..8c88c546 --- /dev/null +++ b/sites/phet_simulations/verify/verify_17 2.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--17. + +Compare translation counts of two named simulations. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--17', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_build_an_atom", navigated_to(t, "/simulation/build-an-atom"), "first detail page opened") + j.check("nav_membrane_transport", navigated_to(t, "/simulation/membrane-transport"), "second detail page opened") + j.check("answer_names_winner", contains_any(fa, ["Build an Atom"]), f"final={fa!r}") + j.check("answer_difference", has_number(fa, 73), f"numbers={numbers_in(fa)}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_17.py b/sites/phet_simulations/verify/verify_17.py new file mode 100644 index 00000000..8c88c546 --- /dev/null +++ b/sites/phet_simulations/verify/verify_17.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--17. + +Compare translation counts of two named simulations. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--17', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_build_an_atom", navigated_to(t, "/simulation/build-an-atom"), "first detail page opened") + j.check("nav_membrane_transport", navigated_to(t, "/simulation/membrane-transport"), "second detail page opened") + j.check("answer_names_winner", contains_any(fa, ["Build an Atom"]), f"final={fa!r}") + j.check("answer_difference", has_number(fa, 73), f"numbers={numbers_in(fa)}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_2 2.py b/sites/phet_simulations/verify/verify_2 2.py new file mode 100644 index 00000000..6da8f46e --- /dev/null +++ b/sites/phet_simulations/verify/verify_2 2.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--2. + +The only Biology sim not offered at university: report its grade levels. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--2', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_biology_catalog", navigated_any(t, ["subject=biology", "/simulations/category/biology"]), + "biology listing visited") + j.check("nav_natural_selection", navigated_to(t, "/simulation/natural-selection"), "target detail page opened") + j.check("answer_names_target", contains_any(fa, ["Natural Selection"]), f"final={fa!r}") + j.check("answer_grades", contains_all(fa, ["element", "middle", "high"]), f"final={fa!r}") + # The task text itself contains the phrase "not offered at university level", so a + # bare substring test would fail a correct answer that restates the question. Bind to + # the page's own rendering of the band instead, and to claims of targeting it. + low = (fa or "").casefold() + claims_university = ("university (ages 18+)" in low + or "ages 18+" in low + or "targets university" in low + or "including university" in low) + j.check("answer_does_not_claim_university", not claims_university, f"final={fa!r}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_2.py b/sites/phet_simulations/verify/verify_2.py new file mode 100644 index 00000000..6da8f46e --- /dev/null +++ b/sites/phet_simulations/verify/verify_2.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--2. + +The only Biology sim not offered at university: report its grade levels. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--2', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_biology_catalog", navigated_any(t, ["subject=biology", "/simulations/category/biology"]), + "biology listing visited") + j.check("nav_natural_selection", navigated_to(t, "/simulation/natural-selection"), "target detail page opened") + j.check("answer_names_target", contains_any(fa, ["Natural Selection"]), f"final={fa!r}") + j.check("answer_grades", contains_all(fa, ["element", "middle", "high"]), f"final={fa!r}") + # The task text itself contains the phrase "not offered at university level", so a + # bare substring test would fail a correct answer that restates the question. Bind to + # the page's own rendering of the band instead, and to claims of targeting it. + low = (fa or "").casefold() + claims_university = ("university (ages 18+)" in low + or "ages 18+" in low + or "targets university" in low + or "including university" in low) + j.check("answer_does_not_claim_university", not claims_university, f"final={fa!r}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_3 2.py b/sites/phet_simulations/verify/verify_3 2.py new file mode 100644 index 00000000..1d9fdd66 --- /dev/null +++ b/sites/phet_simulations/verify/verify_3 2.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--3. + +New release filter: how many of the New sims were released in 2025. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, counts, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--3', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("used_new_filter", navigated_to(t, "release=new"), "release=new in a visited URL") + j.check("answer_count_five", counts(fa, 5, "simulation", "sim", "release", "new"), + f"the count must be reported as a number of simulations; numbers={numbers_in(fa)} final={fa!r}") + j.check("answer_not_twelve", not has_number(fa, 12), "12 is the whole New set, not the 2025 subset") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_3.py b/sites/phet_simulations/verify/verify_3.py new file mode 100644 index 00000000..1d9fdd66 --- /dev/null +++ b/sites/phet_simulations/verify/verify_3.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--3. + +New release filter: how many of the New sims were released in 2025. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, counts, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--3', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("used_new_filter", navigated_to(t, "release=new"), "release=new in a visited URL") + j.check("answer_count_five", counts(fa, 5, "simulation", "sim", "release", "new"), + f"the count must be reported as a number of simulations; numbers={numbers_in(fa)} final={fa!r}") + j.check("answer_not_twelve", not has_number(fa, 12), "12 is the whole New set, not the 2025 subset") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_4 2.py b/sites/phet_simulations/verify/verify_4 2.py new file mode 100644 index 00000000..49c46825 --- /dev/null +++ b/sites/phet_simulations/verify/verify_4 2.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--4. + +Sort by most translated: top simulation and its count. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--4', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("used_translations_sort", navigated_to(t, "sort=translations"), "sort=translations in a visited URL") + j.check("answer_top_title", contains_any(fa, ["Build an Atom"]), f"final={fa!r}") + j.check("answer_top_count", has_number(fa, 104), f"numbers={numbers_in(fa)}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_4.py b/sites/phet_simulations/verify/verify_4.py new file mode 100644 index 00000000..49c46825 --- /dev/null +++ b/sites/phet_simulations/verify/verify_4.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--4. + +Sort by most translated: top simulation and its count. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--4', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("used_translations_sort", navigated_to(t, "sort=translations"), "sort=translations in a visited URL") + j.check("answer_top_title", contains_any(fa, ["Build an Atom"]), f"final={fa!r}") + j.check("answer_top_count", has_number(fa, 104), f"numbers={numbers_in(fa)}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_5 2.py b/sites/phet_simulations/verify/verify_5 2.py new file mode 100644 index 00000000..856ca5d1 --- /dev/null +++ b/sites/phet_simulations/verify/verify_5 2.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--5. + +Most recently released simulation: date and subjects. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--5', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_catalog", navigated_to(t, "/simulations"), "catalogue visited") + j.check("nav_target", navigated_to(t, "/simulation/quantum-wave-interference"), "target detail page opened") + j.check("answer_title", contains_any(fa, ["Quantum Wave Interference"]), f"final={fa!r}") + j.check("answer_release_date", "2026-09-10" in dates_in(fa), f"dates={dates_in(fa)} final={fa!r}") + j.check("answer_subjects", contains_all(fa, ["physics", "chem"]), f"final={fa!r}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_5.py b/sites/phet_simulations/verify/verify_5.py new file mode 100644 index 00000000..856ca5d1 --- /dev/null +++ b/sites/phet_simulations/verify/verify_5.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--5. + +Most recently released simulation: date and subjects. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--5', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_catalog", navigated_to(t, "/simulations"), "catalogue visited") + j.check("nav_target", navigated_to(t, "/simulation/quantum-wave-interference"), "target detail page opened") + j.check("answer_title", contains_any(fa, ["Quantum Wave Interference"]), f"final={fa!r}") + j.check("answer_release_date", "2026-09-10" in dates_in(fa), f"dates={dates_in(fa)} final={fa!r}") + j.check("answer_subjects", contains_all(fa, ["physics", "chem"]), f"final={fa!r}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_6 2.py b/sites/phet_simulations/verify/verify_6 2.py new file mode 100644 index 00000000..fe123fc1 --- /dev/null +++ b/sites/phet_simulations/verify/verify_6 2.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--6. + +Translations page: how many simulations are available in Arabic. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--6', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_translations", navigated_to(t, "/translations"), "translations page visited") + j.check("answer_count", has_number(fa, 119), f"numbers={numbers_in(fa)}") + j.check("answer_not_total", not has_number(fa, 120), "120 is the English/total count, not Arabic") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_6.py b/sites/phet_simulations/verify/verify_6.py new file mode 100644 index 00000000..fe123fc1 --- /dev/null +++ b/sites/phet_simulations/verify/verify_6.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--6. + +Translations page: how many simulations are available in Arabic. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--6', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_translations", navigated_to(t, "/translations"), "translations page visited") + j.check("answer_count", has_number(fa, 119), f"numbers={numbers_in(fa)}") + j.check("answer_not_total", not has_number(fa, 120), "120 is the English/total count, not Arabic") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_7 2.py b/sites/phet_simulations/verify/verify_7 2.py new file mode 100644 index 00000000..47e529cf --- /dev/null +++ b/sites/phet_simulations/verify/verify_7 2.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--7. + +Translations page: the Arabic (Morocco) entry count. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--7', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_translations", navigated_to(t, "/translations"), "translations page visited") + j.check("answer_count", has_number(fa, 103), f"numbers={numbers_in(fa)}") + j.check("answer_not_plain_arabic", not has_number(fa, 119), "119 is plain Arabic, not Arabic (Morocco)") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_7.py b/sites/phet_simulations/verify/verify_7.py new file mode 100644 index 00000000..47e529cf --- /dev/null +++ b/sites/phet_simulations/verify/verify_7.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--7. + +Translations page: the Arabic (Morocco) entry count. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--7', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("nav_translations", navigated_to(t, "/translations"), "translations page visited") + j.check("answer_count", has_number(fa, 103), f"numbers={numbers_in(fa)}") + j.check("answer_not_plain_arabic", not has_number(fa, 119), "119 is plain Arabic, not Arabic (Morocco)") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_8 2.py b/sites/phet_simulations/verify/verify_8 2.py new file mode 100644 index 00000000..a91bb6bb --- /dev/null +++ b/sites/phet_simulations/verify/verify_8 2.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--8. + +Search 'quantum': result count, then the 2026 release's version. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, counts, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--8', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("used_search", navigated_to(t, "/search"), "search used") + j.check("answer_result_count", counts(fa, 7, "result", "simulation", "sim", "hit", "match"), + f"the count must be reported as a number of results; numbers={numbers_in(fa)} final={fa!r}") + j.check("nav_target", navigated_to(t, "/simulation/quantum-wave-interference"), "2026 release opened") + j.check("answer_version", contains_all(fa, ["1.0.0"]), f"final={fa!r}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_8.py b/sites/phet_simulations/verify/verify_8.py new file mode 100644 index 00000000..a91bb6bb --- /dev/null +++ b/sites/phet_simulations/verify/verify_8.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--8. + +Search 'quantum': result count, then the 2026 release's version. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, counts, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--8', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("used_search", navigated_to(t, "/search"), "search used") + j.check("answer_result_count", counts(fa, 7, "result", "simulation", "sim", "hit", "match"), + f"the count must be reported as a number of results; numbers={numbers_in(fa)} final={fa!r}") + j.check("nav_target", navigated_to(t, "/simulation/quantum-wave-interference"), "2026 release opened") + j.check("answer_version", contains_all(fa, ["1.0.0"]), f"final={fa!r}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_9 2.py b/sites/phet_simulations/verify/verify_9 2.py new file mode 100644 index 00000000..40597b9b --- /dev/null +++ b/sites/phet_simulations/verify/verify_9 2.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--9. + +Heat & Thermo topic filter: how many simulations. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, counts, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--9', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("used_topic_filter", navigated_to(t, "topic=heat-and-thermo"), "topic facet used") + j.check("answer_count", counts(fa, 9, "simulation", "sim", "result"), + f"the count must be reported as a number of simulations; numbers={numbers_in(fa)} final={fa!r}") + j.check("answer_names_topic", contains_any(fa, ["Heat", "Thermo"]), + f"the answer must say which topic was counted; final={fa!r}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_9.py b/sites/phet_simulations/verify/verify_9.py new file mode 100644 index 00000000..40597b9b --- /dev/null +++ b/sites/phet_simulations/verify/verify_9.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Deterministic verifier for PhET task PhET Interactive Simulations--9. + +Heat & Thermo topic filter: how many simulations. + +Ground truth is hardcoded here and nowhere in tasks.jsonl. +Input/Output: see verify_lib.parse_args / Judge.emit. +""" +import os, sys +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from verify_lib import (load_run, navigated_to, navigated_any, final_answer, last_shot, + contains_all, contains_any, answer_equals, numbers_in, has_number, + dates_in, resolve_db, saved_sims_for, saved_rows_for, user_exists, + read_only_run, catalog_unchanged, table_counts, db_query, + llm_text_match, counts, Judge, parse_args) + + +def main(): + a = parse_args() + j = Judge('PhET Interactive Simulations--9', a.no_llm) + t = load_run(a.run_dir) + fa = final_answer(t) + j.check("final_answer_nonempty", bool(fa), f"final={fa!r}") + j.check("used_topic_filter", navigated_to(t, "topic=heat-and-thermo"), "topic facet used") + j.check("answer_count", counts(fa, 9, "simulation", "sim", "result"), + f"the count must be reported as a number of simulations; numbers={numbers_in(fa)} final={fa!r}") + j.check("answer_names_topic", contains_any(fa, ["Heat", "Thermo"]), + f"the answer must say which topic was counted; final={fa!r}") + init = resolve_db(a.initial_db, a.container, "instance_seed") + after = resolve_db(a.after_db, a.container, "instance") + ro = read_only_run(init, after) + j.check("run_is_read_only", ro is True, f"read_only={ro}") + j.emit() + + +if __name__ == "__main__": + main() diff --git a/sites/phet_simulations/verify/verify_lib.py b/sites/phet_simulations/verify/verify_lib.py new file mode 100644 index 00000000..263b7724 --- /dev/null +++ b/sites/phet_simulations/verify/verify_lib.py @@ -0,0 +1,378 @@ +#!/usr/bin/env python3 +"""verify_lib.py - shared deterministic + LLM utilities for PhET task verification. + +Philosophy: DETERMINISTIC FIRST. + 1. Trajectory navigation check (anti knowledge-shortcut): the agent MUST have + opened the relevant on-site page; a correct answer with no matching navigation + is a memory-recall shortcut = FAIL. + 2. Answer check: exact / regex / token-containment against frozen ground truth. + 3. DB after-state check (stateful tasks): query the SQLite instance DB directly — + the strongest deterministic signal (saved-word list, registered user row). + 4. LLM utilities (text match, screenshot-contains) are used ONLY where exact + matching is brittle, and are ALWAYS anchored on ground truth: the model + verifies *presence* of given content, it never supplies knowledge. One call each. + +Input signature (per task): + --run_dir DIR agent trajectory dir: trajectory.json + screenshots/step_NNN.png + --initial_db PATH initial-state SQLite DB (default: fetched instance_seed from container) + --after_db PATH after-state SQLite DB (default: fetched live instance DB from container) + --container NAME docker container to fetch DBs from (default: $WH_CONTAINER or wh-review) + --no_llm skip LLM-based checks (run deterministic-only) +Output: JSON {task_id, pass, reason, evidence[]} to stdout; exit 0 on PASS, 1 on FAIL. +""" +import base64, json, os, re, sqlite3, subprocess, sys, tempfile, urllib.request +from pathlib import Path +from dataclasses import dataclass + +SITE = "phet_simulations" + +# ---------------------------------------------------------------- trajectory +def load_run(run_dir): + d = Path(run_dir) + traj = json.loads((d / "trajectory.json").read_text()) + traj["_run_dir"] = d + traj["_shots"] = {p.name: p for p in sorted((d / "screenshots").glob("step_*.png"))} + return traj + +def step_urls(traj): + return [s.get("url", "") for s in traj.get("steps", [])] + +def navigated_to(traj, substr, times=1): + """Deterministic: at least `times` trajectory steps have a URL containing substr.""" + return sum(1 for u in step_urls(traj) if substr in u) >= times + +def navigated_any(traj, substrs): + return any(navigated_to(traj, s) for s in substrs) + +def final_answer(traj): + return (traj.get("final_answer") or "").strip() + +def _shot(traj, name): + if not name: + return None + p = traj["_shots"].get(Path(name).name) + return p if (p and p.exists()) else None + +def shot_after_url(traj, substr): + """screenshot_after path of the first step whose URL contains substr.""" + for s in traj.get("steps", []): + if substr in s.get("url", ""): + p = _shot(traj, s.get("screenshot_after")) + if p: + return p + return None + +def last_shot(traj): + for s in reversed(traj.get("steps", [])): + p = _shot(traj, s.get("screenshot_after")) or _shot(traj, s.get("screenshot_before")) + if p: + return p + shots = sorted(traj["_shots"].values()) + return shots[-1] if shots else None + +# ---------------------------------------------------------------- deterministic answer match +def norm(s): + return re.sub(r"\s+", " ", (s or "").strip()).casefold() + +def answer_equals(final, expected): + return norm(final) == norm(expected) + +def contains_all(final, tokens): + f = norm(final) + return all(norm(t) in f for t in tokens) + +def contains_any(final, tokens): + f = norm(final) + return any(norm(t) in f for t in tokens) + +def extract_years(text): + return re.findall(r"\b(1[5-9]\d{2}|20\d{2})\b", text or "") + +def extract_score(text): + m = re.search(r"(\d+)\s*/\s*10", text or "") + return m.group(1) if m else None + +# ---------------------------------------------------------------- DB state +def fetch_db(container, kind): + """kind: 'instance' (after-state) or 'instance_seed' (initial-state). docker cp -> temp file.""" + src = f"{container}:/opt/WebSyn/{SITE}/{kind}/{SITE}.db" + fd, path = tempfile.mkstemp(suffix=".db") + os.close(fd) + r = subprocess.run(["docker", "cp", src, path], capture_output=True, text=True) + if r.returncode != 0: + try: + os.unlink(path) + except OSError: + pass + raise RuntimeError(f"docker cp {src} failed: {r.stderr.strip()}") + return path + +def resolve_db(arg, container, kind): + if arg: + return arg + try: + return fetch_db(container, kind) + except Exception: + return None # caller treats None as "unavailable" and FAILs that check + +def db_query(db_path, sql, params=()): + con = sqlite3.connect(db_path) + try: + return con.execute(sql, params).fetchall() + finally: + con.close() + +def saved_sims_for(db_path, email="teacher@phet.test"): + """Slugs saved to an account, ordered. None when the DB is unavailable.""" + if not db_path: + return None + rows = db_query(db_path, + "SELECT s.slug FROM saved_simulation ss JOIN user u ON u.id=ss.user_id " + "JOIN simulation s ON s.id=ss.sim_id WHERE u.email=? ORDER BY s.slug", (email,)) + return [r[0] for r in rows] + + +def saved_rows_for(db_path, email="teacher@phet.test"): + """(slug, notes) pairs saved to an account.""" + if not db_path: + return None + return db_query(db_path, + "SELECT s.slug, COALESCE(ss.notes, '') FROM saved_simulation ss " + "JOIN user u ON u.id=ss.user_id JOIN simulation s ON s.id=ss.sim_id " + "WHERE u.email=? ORDER BY s.slug", (email,)) + + +def user_exists(db_path, name=None, email=None): + if not db_path: + return None + rows = db_query(db_path, "SELECT name, email FROM user") + return any((name is None or r[0] == name) and (email is None or r[1] == email) + for r in rows) + + +def table_counts(db_path, tables=("simulation", "language", "subject", "grade_level", + "activity", "user", "saved_simulation")): + """Row counts for the runtime tables, used for read-only and exact-delta checks.""" + if not db_path: + return None + out = {} + for tbl in tables: + try: + out[tbl] = db_query(db_path, f"SELECT COUNT(*) FROM {tbl}")[0][0] + except sqlite3.Error: + out[tbl] = None + return out + + +def catalog_unchanged(initial_db, after_db): + """The catalogue tables an agent must never be able to write.""" + if not initial_db or not after_db: + return None + for tbl in ("simulation", "language", "subject", "grade_level", "activity"): + a = db_query(initial_db, f"SELECT COUNT(*) FROM {tbl}")[0][0] + b = db_query(after_db, f"SELECT COUNT(*) FROM {tbl}")[0][0] + if a != b: + return False + return True + + +def read_only_run(initial_db, after_db): + """True when no runtime row changed at all (used by look-up only tasks).""" + if not initial_db or not after_db: + return None + for tbl in ("user", "saved_simulation"): + a = db_query(initial_db, f"SELECT COUNT(*) FROM {tbl}")[0][0] + b = db_query(after_db, f"SELECT COUNT(*) FROM {tbl}")[0][0] + if a != b: + return False + return catalog_unchanged(initial_db, after_db) + + +_WORD_NUMBERS = { + "zero": 0, "one": 1, "two": 2, "three": 3, "four": 4, "five": 5, "six": 6, + "seven": 7, "eight": 8, "nine": 9, "ten": 10, "eleven": 11, "twelve": 12, + "thirteen": 13, "fourteen": 14, "fifteen": 15, "sixteen": 16, + "seventeen": 17, "eighteen": 18, "nineteen": 19, "twenty": 20, +} + + +def numbers_in(text): + """Integers in the answer, as digits or as English number words. + + An agent that writes "Five." is as correct as one that writes "5", so both + forms are accepted. Commas inside digit groups are tolerated. + """ + out = [int(m.replace(",", "")) for m in re.findall(r"\b\d[\d,]*\b", text or "")] + for word, value in _WORD_NUMBERS.items(): + if re.search(rf"\b{word}\b", text or "", re.IGNORECASE): + out.append(value) + return out + + +def has_number(text, value): + return value in numbers_in(text) + + +def counts(text, value, *nouns): + """True when `value` is reported AS A COUNT of one of `nouns`. + + has_number alone accepts a digit appearing anywhere, so an answer about a + different subject ("version 9.9.9") satisfies a check for 9. This binds the + number to its referent while still accepting the natural phrasings an agent + uses: "9 simulations", "9 sims", "nine simulations", "simulations: 9", + "contains 9", "there are 9". + """ + t = (text or "").lower() + if value not in numbers_in(text): + return False + words = {v: k for k, v in _WORD_NUMBERS.items()} + forms = [str(value)] + ([words[value]] if value in words else []) + for noun in [n.lower() for n in nouns]: + for f in forms: + pats = [rf"{re.escape(f)}\s*(?:\w+\s+){{0,3}}{re.escape(noun)}", + rf"{re.escape(noun)}[^.]{{0,40}}?\b{re.escape(f)}\b"] + if any(re.search(p, t) for p in pats): + return True + return False + + +def dates_in(text): + """ISO and common long-form dates, normalised to YYYY-MM-DD where possible.""" + out = list(re.findall(r"\b(\d{4}-\d{2}-\d{2})\b", text or "")) + months = {m: f"{i+1:02d}" for i, m in enumerate( + ["january", "february", "march", "april", "may", "june", "july", + "august", "september", "october", "november", "december"])} + for mon, day, year in re.findall( + r"\b([A-Za-z]+)\s+(\d{1,2}),?\s+(\d{4})\b", text or ""): + key = mon.casefold() + if key in months: + out.append(f"{year}-{months[key]}-{int(day):02d}") + for day, mon, year in re.findall( + r"\b(\d{1,2})\s+([A-Za-z]+)\s+(\d{4})\b", text or ""): + key = mon.casefold() + if key in months: + out.append(f"{year}-{months[key]}-{int(day):02d}") + return out + + +# ---------------------------------------------------------------- shared LLM utilities (anchored) +# Unified LLM config, same env vars as agent.py / eval_judge.py: +# OPENAI_API_KEY, OPENAI_BASE_URL, JUDGE_MODEL +import simpleArgParser as sap + +# When --no_llm is set (via Judge), the llm_* helpers short-circuit so verifiers +# that call them directly (before j.check(llm=True)) still make ZERO LLM calls. +_NO_LLM = False + + +def _llm_config(): + """Resolve (api_key, api_base, model) from env once per process.""" + key = os.environ.get("OPENAI_API_KEY", "") + base = os.environ.get("OPENAI_BASE_URL", "") + model = os.environ.get("JUDGE_MODEL", "") + return key, base, model + + +def _chat(messages, max_tokens=1024): + """One LLM call against the configured OpenAI-compatible endpoint. Returns text or None.""" + if _NO_LLM: + return None + key, base, model = _llm_config() + if not (key and base and model): + return None # no LLM configured -> callers treat as non-PASS + payload = {"model": model, "messages": messages, + "max_tokens": max_tokens, "temperature": 1.0} + req = urllib.request.Request(base, + data=json.dumps(payload).encode(), + headers={"Content-Type": "application/json", + "Authorization": f"Bearer {key}"}) + try: + data = json.loads(urllib.request.urlopen(req, timeout=180).read()) + except Exception: + return None # caller treats None as a non-PASS; never raises + try: + return data["choices"][0]["message"]["content"] + except Exception: + return None + +def _verdict(out): + """Normalize an LLM reply to (pass_bool, text). None/empty -> (False, '').""" + if not out: + return False, "" + s = out.strip() + return s.upper().startswith("PASS"), s + +def llm_text_match(agent_answer, ground_truth, question): + """One LLM call: does agent_answer correctly answer question AND stay consistent + with the frozen ground truth? The model is given the ground truth as an anchor + and is told NOT to use its own knowledge.""" + if _NO_LLM: + return False, "[skipped: --no_llm]" + out = _chat([{"role": "user", "content": + f"You are a STRICT binary grader.\nQuestion: {question}\n" + f"Ground-truth answer (ANCHOR — judge against THIS, never use your own knowledge): {ground_truth}\n" + f"Agent's answer: {agent_answer}\n" + f"Decide PASS or FAIL ignoring case/punctuation/word order/surrounding prose. " + f"PASS only if the agent's answer is consistent with the ground truth AND actually answers the question. " + f"Line 1: PASS or FAIL. Line 2: one-sentence reason."}]) + return _verdict(out) + +def llm_screenshot_shows(shot_path, must_show, question=""): + """One vision LLM call: does this screenshot visibly render text answering/containing + `must_show`? The model judges pixels only, anchored on the expected content.""" + if _NO_LLM: + return False, "[skipped: --no_llm]" + b64 = base64.b64encode(Path(shot_path).read_bytes()).decode() + out = _chat([{"role": "user", "content": [ + {"type": "text", "text": + f"You are a STRICT binary grader. Only what is VISIBLY rendered in this screenshot counts.\n" + f"Question the page should answer: {question}\n" + f"Expected content to verify PRESENCE of: {must_show}\n" + f"PASS only if the expected content (or a semantically equivalent on-screen answer) is visibly shown. " + f"Do NOT use prior knowledge — judge only the rendered pixels.\n" + f"Line 1: PASS or FAIL. Line 2: quote the visible evidence."}, + {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{b64}"}}]}]) + return _verdict(out) + +# ---------------------------------------------------------------- judge harness + CLI +class Judge: + def __init__(self, task_id, no_llm=False): + global _NO_LLM + _NO_LLM = bool(no_llm) # gate the llm_* helpers at the source + self.task_id = task_id + self.no_llm = no_llm + self.ok = True + self.reason = "" + self.evidence = [] + + def check(self, name, cond, evidence="", llm=False): + if llm and self.no_llm: + self.evidence.append(f"[SKIP] {name} (--no-llm)") + return True + if cond: + self.evidence.append(f"[PASS] {name}: {evidence}") + else: + self.ok = False + if not self.reason: + self.reason = name # record the FIRST failing check + self.evidence.append(f"[FAIL] {name}: {evidence}") + return bool(cond) + + def emit(self): + print(json.dumps({"task_id": self.task_id, "pass": self.ok, + "reason": self.reason, "evidence": self.evidence}, indent=2)) + sys.exit(0 if self.ok else 1) + +def parse_args(): + @dataclass + class VerifyArgs: + run_dir: str = "" + initial_db: str = "" + after_db: str = "" + container: str = os.environ.get("WH_CONTAINER", "wh-review") + no_llm: bool = False + + def post_process(self): + if not self.run_dir: + raise SystemExit("--run_dir is required") + return sap.parse_args(VerifyArgs) diff --git a/websyn_start.sh b/websyn_start.sh index 941bf323..c63955a7 100644 --- a/websyn_start.sh +++ b/websyn_start.sh @@ -7,7 +7,7 @@ SITES=(allrecipes amazon apple arxiv bbc_news booking github google_flights google_map google_search huggingface wolfram_alpha cambridge_dictionary coursera espn merriam_webster ikea phys_org target ted osu rotten_tomatoes compass walmart_careers - fedex webmd_doctor healthline kaggle nvidia) + fedex webmd_doctor healthline kaggle nvidia phet_simulations) BASE_PORT=40000 SITE_COUNT=${#SITES[@]} PID_DIR=/tmp/websyn_pids